Skip to main content

sequoia_wot/
network.rs

1use std::borrow::Borrow;
2use std::collections::BTreeMap;
3use std::fmt;
4use std::time::SystemTime;
5use std::ops::Deref;
6
7use sequoia_openpgp as openpgp;
8
9use openpgp::Result;
10use openpgp::cert::prelude::*;
11use openpgp::cert::raw::RawCert;
12use openpgp::Fingerprint;
13use openpgp::packet::UserID;
14use openpgp::policy::Policy;
15
16use sequoia_cert_store as cert_store;
17
18use crate::CertSynopsis;
19use crate::Certification;
20use crate::FULLY_TRUSTED;
21use crate::Path;
22use crate::Paths;
23use crate::RevocationStatus;
24use crate::store::CertStore;
25use crate::store::Store;
26use crate::store::SynopsisSlice;
27
28pub(crate) mod filter;
29use filter::CapCertificateFilter;
30use filter::CapDepthFilter;
31use filter::ChainFilter;
32use filter::SuppressIssuerFilter;
33use filter::SuppressCertificationFilter;
34use filter::TrustedIntroducerFilter;
35mod root;
36pub use root::Root;
37mod roots;
38pub use roots::Roots;
39mod path;
40pub use path::PathError;
41pub use path::CertLints;
42pub use path::CertificationLints;
43pub use path::PathLints;
44mod builder;
45pub use builder::NetworkBuilder;
46
47use super::TRACE;
48
49/// A certification network.
50pub struct Network<S>
51    where S: Store
52{
53    store: S,
54
55    // The trust roots.
56    roots: Roots,
57
58    // If this is a certification network (where all certificates are
59    // considered tsigs with infinite depth and no regular
60    // expression), or a normal authentication network.
61    certification_network: bool,
62
63    /// Whether to constrain the search to paths with a given depth.
64    maximum_depth: Option<usize>,
65}
66
67impl<S> fmt::Debug for Network<S>
68    where S: Store
69{
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        write!(f, "Network {{\n")?;
72        write!(f, "  Reference time: {:?}\n", self.reference_time())?;
73        write!(f, "  Nodes:\n")?;
74
75        let mut certs: Vec<_> = self.synopses().map(|cert| {
76            (
77                cert.self_signed_and_revoked_userids()
78                    .map(|userid| {
79                        format!(
80                            "{}{}",
81                            String::from_utf8_lossy(userid.value()),
82                            match userid.revocation_status() {
83                                RevocationStatus::NotAsFarAsWeKnow => "",
84                                RevocationStatus::Soft(_t) => " (soft revoked)",
85                                RevocationStatus::Hard => " (hard revoked)",
86                            })
87                    })
88                    .collect::<Vec<String>>()
89                    .join(", "),
90                cert.fingerprint()
91            )
92        }).collect();
93        certs.sort();
94
95        for (userid, fpr) in certs {
96            write!(f, "    {}: {}\n", fpr, userid)?;
97        }
98
99        write!(f, "  Edges:\n")?;
100
101        let mut certifications: Vec<crate::CertificationSet> = self
102            .iter_fingerprints()
103            .filter_map(|fpr| {
104                if let Ok(cs) = self.certifications_of(&fpr, 0.into()) {
105                    if cs.is_empty() {
106                        None
107                    } else {
108                        Some((*cs).clone())
109                    }
110                } else {
111                    None
112                }
113            })
114            .flatten()
115            .collect::<Vec<_>>();
116        certifications.sort_by_key(|cs| {
117            (cs.issuer().primary_userid().map(|u| u.userid().clone()),
118             cs.issuer().fingerprint(),
119             cs.target().fingerprint())
120        });
121
122        let mut last_issuer_fpr = None;
123        for cs in certifications.into_iter() {
124            let issuer = &cs.issuer();
125            let issuer_fpr = issuer.fingerprint();
126            if Some(&issuer_fpr) != last_issuer_fpr.as_ref() {
127                write!(f, "    {} certifies:\n", issuer)?;
128                last_issuer_fpr = Some(issuer_fpr);
129            }
130
131            let target_fpr = cs.target().fingerprint();
132            for c in cs.into_certifications() {
133                write!(f, "      {}, {}: {}, {}, {}\n",
134                       target_fpr,
135                       c.userid().map(|userid| {
136                           String::from_utf8_lossy(userid.value()).into_owned()
137                       }).unwrap_or_else(|| "<No User ID>".into()),
138                       c.depth(), c.amount(),
139                       if let Some(re_set) = c.regular_expressions() {
140                           if re_set.matches_everything() {
141                               "*".into()
142                           } else {
143                               format!("{:?}", re_set)
144                           }
145                       } else {
146                           "<invalid RE>".into()
147                       })?;
148            }
149        }
150
151        write!(f, "}}\n")?;
152
153        Ok(())
154    }
155}
156
157impl<S> Deref for Network<S>
158    where S: Store
159{
160    type Target = S;
161
162    fn deref(&self) -> &Self::Target {
163        &self.store
164    }
165}
166
167impl<S> Network<S>
168    where S: Store
169{
170    /// Returns a rooted Network.
171    ///
172    /// By default, the `Network` is an authentication network.  In
173    /// this mode of operation, plain certifications are only
174    /// considered certifications, and the target is not considered to
175    /// be a trusted introducer.  An alternative mode of operation is
176    /// a certification network.  This can be configured using
177    /// [`NetworkBuilder::certification_network`].
178    pub fn new<R>(store: S, roots: R)
179        -> Result<Self>
180        where R: Into<Roots>,
181    {
182        tracer!(TRACE, "Network::new");
183
184        let roots = roots.into();
185
186        t!("Roots ({}): {}.",
187           roots.iter().count(),
188           roots.iter()
189               .map(|r| format!("{} ({})", r.fingerprint(), r.amount()))
190               .collect::<Vec<_>>()
191               .join(", "));
192
193        Ok(NetworkBuilder::rooted(store, roots).build())
194    }
195
196    /// Returns a reference to the underlying store.
197    pub fn backend(&self) -> &S {
198        &self.store
199    }
200}
201
202impl<'a: 'policy, 'policy> Network<CertStore<'a, 'policy, cert_store::store::Certs<'a>>> {
203    /// Builds a web of trust network from a set of certificates.
204    ///
205    /// If a certificate is invalid according to the policy, the
206    /// certificate is silently ignored.
207    pub fn from_certs<I, C, T, R>(certs: I,
208                                  policy: &'policy dyn Policy, t: T,
209                                  roots: R)
210        -> Result<Self>
211    where T: Into<Option<SystemTime>>,
212          I: IntoIterator<Item=C>,
213          C: Into<Cert>,
214          R: Into<Roots>,
215    {
216        tracer!(TRACE, "Network::from_certs");
217
218        let t = t.into().unwrap_or_else(|| crate::now());
219        Network::new(
220            CertStore::from_certs(
221                certs.into_iter().map(|c| c.into()),
222                policy, t)?,
223            roots)
224    }
225
226    /// Builds a web of trust network from a set of certificates.
227    ///
228    /// If a certificate is invalid according to the policy, the
229    /// certificate is silently ignored.
230    pub fn from_cert_refs<I, C, T, R>(certs: I,
231                                      policy: &'policy dyn Policy, t: T,
232                                      roots: R)
233        -> Result<Self>
234    where T: Into<Option<SystemTime>>,
235          I: IntoIterator<Item=C>,
236          C: Into<&'a Cert>,
237          R: Into<Roots>,
238    {
239        tracer!(TRACE, "Network::from_certs");
240
241        let t = t.into().unwrap_or_else(|| crate::now());
242        Network::new(
243            CertStore::from_cert_refs(
244                certs.into_iter().map(|c| c.into()),
245                policy, t)?,
246            roots)
247    }
248
249    /// Builds a web of trust network from a keyring.
250    ///
251    /// If a certificate is invalid according to the policy, the
252    /// certificate is silently ignored.
253    pub fn from_bytes<T, R>(certs: &'a [u8], policy: &'policy dyn Policy, t: T,
254                            roots: R)
255        -> Result<Self>
256    where T: Into<Option<SystemTime>>,
257          R: Into<Roots>,
258    {
259        tracer!(TRACE, "Network::from_bytes");
260
261        let t = t.into().unwrap_or_else(|| crate::now());
262        Network::new(CertStore::from_bytes(certs, policy, t)?, roots)
263    }
264
265    /// Builds a web of trust network from a set of raw certificates.
266    ///
267    /// If a certificate is invalid according to the policy, the
268    /// certificate is silently ignored.
269    pub fn from_raw_certs<T, R>(certs: impl Iterator<Item=RawCert<'a>>,
270                                policy: &'a dyn Policy, t: T,
271                                roots: R)
272        -> Result<Self>
273    where T: Into<Option<SystemTime>>,
274          R: Into<Roots>,
275    {
276        tracer!(TRACE, "Network::from_raw_certs");
277
278        let t = t.into().unwrap_or_else(|| crate::now());
279        Network::new(
280            CertStore::from_raw_certs(certs, policy, t)?,
281            roots)
282    }
283}
284
285impl<'a> Network<SynopsisSlice<'a>> {
286    /// Builds a web of trust network from a set of certificates.
287    ///
288    /// If a certificate is invalid according to the policy, the
289    /// certificate is silently ignored.
290    pub fn from_synopses<R>(certs: &'a [CertSynopsis],
291                            certifications: &'a [Certification],
292                            t: SystemTime,
293                            roots: R)
294        -> Result<Self>
295        where R: Into<Roots>
296    {
297        Network::new(
298            SynopsisSlice::new(certs, certifications, t)?,
299            roots)
300    }
301}
302
303impl<S> Network<S>
304    where S: Store
305{
306    /// Returns a reference to the roots.
307    pub fn roots(&self) -> &Roots
308    {
309        &self.roots
310    }
311
312    /// Returns whether the specified certificate is a root.
313    pub fn is_root<F>(&self, fpr: F) -> bool
314        where F: Borrow<Fingerprint>
315    {
316        self.roots.is_root(fpr.borrow())
317    }
318
319    /// Returns the specified root.
320    pub fn root<F>(&self, fpr: F) -> Option<&Root>
321        where F: Borrow<Fingerprint>
322    {
323        self.roots.get(fpr.borrow())
324    }
325
326    /// Returns whether the `Network` is a certification network.
327    ///
328    /// See [`NetworkBuilder::certification_network`] for
329    /// details.
330    pub fn certification_network(&self) -> bool {
331        self.certification_network
332    }
333
334    /// Returns whether the `Network` is an authentication network.
335    ///
336    /// See [`NetworkBuilder::certification_network`] for
337    /// details.
338    pub fn authentication_network(&self) -> bool {
339        ! self.certification_network
340    }
341
342    /// Returns the maximum depth.
343    ///
344    /// With the depth limited to `0`, the maximum lengths of paths
345    /// will be two, with the paths containing the certifier and the
346    /// target).
347    pub fn maximum_depth(&mut self) -> Option<usize> {
348        self.maximum_depth
349    }
350
351    fn authenticate_internal<U, F>(&self, target_userid: U, target_fpr: F,
352                                   target_trust_amount: usize,
353                                   gossip: bool)
354        -> Paths
355    where U: Borrow<UserID>,
356          F: Borrow<Fingerprint>,
357    {
358        tracer!(TRACE, "Network::authenticate_internal");
359
360        let target_userid = target_userid.borrow();
361        let target_fpr = target_fpr.borrow();
362
363        t!("Authenticating <{}, {}>",
364           target_fpr, String::from_utf8_lossy(target_userid.value()));
365        t!("Roots ({}):", self.roots.iter().count());
366        for (i, r) in self.roots.iter().enumerate() {
367            t!("  {}: {} ({})", i, r.fingerprint(), r.amount());
368        }
369
370        let mut paths = Paths::new();
371
372        let mut filter = ChainFilter::new();
373        if self.certification_network {
374            // We're building a certification network: treat all
375            // certifications like tsigs with infinite depth and no
376            // regular expressions.
377            filter.push(TrustedIntroducerFilter::new());
378        } else {
379            if self.roots.iter().any(|r| r.amount() != FULLY_TRUSTED) {
380                let mut caps = CapCertificateFilter::new();
381                for r in self.roots.iter() {
382                    let amount = r.amount();
383                    if amount != FULLY_TRUSTED  {
384                        caps.cap(r.fingerprint().clone(), amount);
385                    }
386                }
387                filter.push(caps);
388            };
389        }
390
391        // Limit the path length.  Note: It is important to push this
392        // filter after the TrustedIntroducerFilter, which
393        // unconditionally sets the depth to unconstrained.
394        if let Some(limit) = self.maximum_depth {
395            filter.push(CapDepthFilter::new(limit));
396        }
397
398        let mut progress = true;
399        'next_path: while progress
400            && (paths.amount() < target_trust_amount || gossip)
401        {
402            progress = false;
403
404            let mut gossip_paths = Vec::new();
405
406            for self_signed in [true, false] {
407                let auth_paths: BTreeMap<Fingerprint, (Path, usize)>
408                    = self.backward_propagate(
409                        target_fpr.clone(), target_userid.clone(),
410                        self_signed, &filter, gossip);
411
412                // Note: the paths returned by backward_propagate may
413                // overlap.  As such, we can only take one.  (Or we need
414                // to subtract any overlap.  But that is fragile.)  Then
415                // we subtract the path from the network and run
416                // backward_propagate again, if necessary.
417                if let Some((path, path_amount)) = self.roots.iter()
418                    // Get the paths that start at the roots.
419                    .filter_map(|r| {
420                        auth_paths.get(r.fingerprint())
421                    })
422                    // Choose the one that: has the maximum amount of
423                    // trust.  If there are multiple such paths, prefer
424                    // the shorter one.
425                    .max_by_key(|(path, path_amount)| {
426                        (// We want the *most* amount of trust,
427                            path_amount,
428                            // but the *shortest* path.
429                            -(path.len() as isize),
430                            // Be predictable.  Break ties based on the
431                            // fingerprint of the root.
432                            path.root().fingerprint())
433                    })
434                {
435                    let path = path.clone();
436
437                    if path.len() == 1 {
438                        // It's a root.
439                        let mut suppress_filter
440                            = SuppressIssuerFilter::new();
441                        suppress_filter.suppress_issuer(
442                            &path.root().fingerprint(), *path_amount);
443                        filter.push(suppress_filter);
444                    } else {
445                        // Add the path to the filter to create a residual
446                        // network without this path.
447                        let mut suppress_filter
448                            = SuppressCertificationFilter::new();
449                        suppress_filter.suppress_path(&path, *path_amount);
450                        filter.push(suppress_filter);
451                    }
452
453                    paths.push(path, *path_amount);
454                    progress = true;
455                    // Prefer paths where the target User ID is self
456                    // signed as long as possible.
457                    continue 'next_path;
458                } else if gossip {
459                    gossip_paths.extend(auth_paths.into_values());
460                }
461            }
462
463            // No authenticated paths left.
464            assert!(! progress);
465
466            if gossip {
467                // We're looking for gossip paths.  Add the remaining
468                // paths.  But, don't add paths that are just suffixes
469                // of other paths.  To make this easier, we add the
470                // longest paths first so that shorter suffixes are
471                // filtered out when we try to add them.
472                t!("Adding the remaining paths ({}) as gossip paths",
473                   gossip_paths.len());
474
475                gossip_paths.sort_by_key(|(path, _amount)| {
476                    -(path.len() as isize)
477                });
478                for (path, _amount) in gossip_paths.into_iter() {
479                    if ! paths.has_suffix(&path) {
480                        t!("Adding: {:?} (length: {})", path, path.len());
481                        paths.push(path, 0);
482                    } else {
483                        t!("Skipping suffix: {:?}", path);
484                    }
485                }
486            }
487        }
488
489        paths
490    }
491
492    /// Authenticates the specified binding.
493    ///
494    /// Enough independent paths are gotten to satisfy
495    /// `target_trust_amount`.  A fully trusted authentication is 120.
496    /// If you require that a binding be double authenticated, you can
497    /// specify 240.
498    pub fn authenticate<U, F>(&self, target_userid: U, target_fpr: F,
499                              target_trust_amount: usize)
500        -> Paths
501    where U: Borrow<UserID>,
502          F: Borrow<Fingerprint>,
503    {
504        self.authenticate_internal(target_userid, target_fpr,
505                                   target_trust_amount, false)
506    }
507
508    /// Gets gossip about the specified binding.
509    ///
510    /// This is like [`Network::authenticate`], but it also includes
511    /// all unauthenticated paths to the target binding.  The
512    /// aggregate trust amount is accurate.
513    ///
514    /// Note: the paths are dedup based on whether they are a suffix
515    /// of another path.  That is, if `A -> B -> C` is a valid gossip
516    /// path, then so is `B -> C`.
517    pub fn gossip<U, F>(&self, target_fpr: F, target_userid: U)
518        -> Paths
519    where U: Borrow<UserID>,
520          F: Borrow<Fingerprint>,
521    {
522        self.authenticate_internal(target_userid, target_fpr,
523                                   0, true)
524    }
525}
526
527#[cfg(test)]
528mod test {
529    use super::*;
530
531    use std::slice;
532    use std::time;
533
534    use openpgp::Fingerprint;
535    use openpgp::packet::UserID;
536    use openpgp::parse::Parse;
537    use openpgp::policy::StandardPolicy;
538
539    #[allow(unused)]
540    #[test]
541    fn third_party_certifications_of() -> Result<()> {
542        let p = &StandardPolicy::new();
543
544        let alice_fpr: Fingerprint =
545            "2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA"
546           .parse().expect("valid fingerprint");
547        let alice_uid
548            = UserID::from("<alice@example.org>");
549
550        let bob_fpr: Fingerprint =
551            "03182611B91B1E7E20B848E83DFC151ABFAD85D5"
552           .parse().expect("valid fingerprint");
553        let bob_uid
554            = UserID::from("<bob@other.org>");
555        // Certified by: 2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA
556        let bob_some_org_uid
557            = UserID::from("<bob@some.org>");
558        // Certified by: 2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA
559
560        let carol_fpr: Fingerprint =
561            "9CA36907B46FE7B6B9EE9601E78064C12B6D7902"
562           .parse().expect("valid fingerprint");
563        let carol_uid
564            = UserID::from("<carol@example.org>");
565        // Certified by: 03182611B91B1E7E20B848E83DFC151ABFAD85D5
566
567        let dave_fpr: Fingerprint =
568            "C1BC6794A6C6281B968A6A41ACE2055D610CEA03"
569           .parse().expect("valid fingerprint");
570        let dave_uid
571            = UserID::from("<dave@other.org>");
572        // Certified by: 9CA36907B46FE7B6B9EE9601E78064C12B6D7902
573
574
575        let certs: Vec<Cert> = CertParser::from_bytes(
576            &crate::testdata::data("multiple-userids-1.pgp"))?
577            .map(|c| c.expect("Valid certificate"))
578            .collect();
579        let store = CertStore::from_cert_refs(
580            certs.iter().map(|c| c.into()), p, None)?;
581        let n = NetworkBuilder::rootless(store).build();
582
583        eprintln!("{:?}", n);
584
585        // No one certified alice.
586        assert!(
587            n.third_party_certifications_of(&alice_fpr.clone())
588                .is_empty());
589
590        // Alice (and no one else) certified each of Bob's User IDs.
591        let mut c = n.third_party_certifications_of(&bob_fpr);
592        assert_eq!(c.len(), 2);
593        c.sort_by_key(|c| (c.issuer().fingerprint(),
594                           c.userid().map(Clone::clone)));
595        assert_eq!(&c[0].issuer().fingerprint(), &alice_fpr);
596        assert_eq!(c[0].userid(), Some(&bob_uid));
597        assert_eq!(&c[1].issuer().fingerprint(), &alice_fpr);
598        assert_eq!(c[1].userid(), Some(&bob_some_org_uid));
599
600        Ok(())
601    }
602
603    #[allow(unused)]
604    #[test]
605    fn certified_userids_of() -> Result<()> {
606        let p = &StandardPolicy::new();
607
608        let alice_fpr: Fingerprint =
609            "2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA"
610           .parse().expect("valid fingerprint");
611        let alice_uid
612            = UserID::from("<alice@example.org>");
613
614        let bob_fpr: Fingerprint =
615            "03182611B91B1E7E20B848E83DFC151ABFAD85D5"
616           .parse().expect("valid fingerprint");
617        let bob_uid
618            = UserID::from("<bob@other.org>");
619        // Certified by: 2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA
620        let bob_some_org_uid
621            = UserID::from("<bob@some.org>");
622        // Certified by: 2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA
623
624        let carol_fpr: Fingerprint =
625            "9CA36907B46FE7B6B9EE9601E78064C12B6D7902"
626           .parse().expect("valid fingerprint");
627        let carol_uid
628            = UserID::from("<carol@example.org>");
629        // Certified by: 03182611B91B1E7E20B848E83DFC151ABFAD85D5
630
631        let dave_fpr: Fingerprint =
632            "C1BC6794A6C6281B968A6A41ACE2055D610CEA03"
633           .parse().expect("valid fingerprint");
634        let dave_uid
635            = UserID::from("<dave@other.org>");
636        // Certified by: 9CA36907B46FE7B6B9EE9601E78064C12B6D7902
637
638
639        let certs: Vec<Cert> = CertParser::from_bytes(
640            &crate::testdata::data("multiple-userids-1.pgp"))?
641            .map(|c| c.expect("Valid certificate"))
642            .collect();
643        let store = CertStore::from_cert_refs(
644            certs.iter().map(|c| c.into()), p, None)?;
645        let n = NetworkBuilder::rootless(store).build();
646
647        eprintln!("{:?}", n);
648
649        // There is the self signature.
650        let mut c = n.certified_userids_of(&alice_fpr);
651        assert_eq!(c.len(), 1);
652
653        // Alice (and no one else) certified each of Bob's User IDs
654        // for the two self signed User ID.
655        let mut c = n.certified_userids_of(&bob_fpr);
656        assert_eq!(c.len(), 2);
657        c.sort_unstable();
658        assert_eq!(&c[0], &bob_uid);
659        assert_eq!(&c[1], &bob_some_org_uid);
660
661        Ok(())
662    }
663
664    #[allow(unused)]
665    #[test]
666    fn certified_userids() -> Result<()> {
667        let p = &StandardPolicy::new();
668
669        let alice_fpr: Fingerprint =
670            "2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA"
671           .parse().expect("valid fingerprint");
672        let alice_uid
673            = UserID::from("<alice@example.org>");
674
675        let bob_fpr: Fingerprint =
676            "03182611B91B1E7E20B848E83DFC151ABFAD85D5"
677           .parse().expect("valid fingerprint");
678        let bob_uid
679            = UserID::from("<bob@other.org>");
680        // Certified by: 2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA
681        let bob_some_org_uid
682            = UserID::from("<bob@some.org>");
683        // Certified by: 2A2A4A23A7EEC119BC0B46642B3825DC02A05FEA
684
685        let carol_fpr: Fingerprint =
686            "9CA36907B46FE7B6B9EE9601E78064C12B6D7902"
687           .parse().expect("valid fingerprint");
688        let carol_uid
689            = UserID::from("<carol@example.org>");
690        // Certified by: 03182611B91B1E7E20B848E83DFC151ABFAD85D5
691
692        let dave_fpr: Fingerprint =
693            "C1BC6794A6C6281B968A6A41ACE2055D610CEA03"
694           .parse().expect("valid fingerprint");
695        let dave_uid
696            = UserID::from("<dave@other.org>");
697        // Certified by: 9CA36907B46FE7B6B9EE9601E78064C12B6D7902
698
699
700        let certs: Vec<Cert> = CertParser::from_bytes(
701            &crate::testdata::data("multiple-userids-1.pgp"))?
702            .map(|c| c.expect("Valid certificate"))
703            .collect();
704        let store = CertStore::from_cert_refs(
705            certs.iter().map(|c| c.into()), p, None)?;
706        let n = NetworkBuilder::rootless(store).build();
707
708        eprintln!("{:?}", n);
709
710        // Alice is the root, but self signatures count, so there are
711        // five certified User IDs in this network.
712        let mut got = n.certified_userids();
713        assert_eq!(got.len(), 5);
714
715        got.sort_unstable();
716
717        let mut expected = [
718            (alice_fpr.clone(), alice_uid.clone()),
719            (bob_fpr.clone(), bob_uid.clone()),
720            (bob_fpr.clone(), bob_some_org_uid.clone()),
721            (carol_fpr.clone(), carol_uid.clone()),
722            (dave_fpr.clone(), dave_uid.clone()),
723        ];
724        expected.sort_unstable();
725
726        assert_eq!(got, expected);
727
728        Ok(())
729    }
730
731    #[allow(unused)]
732    #[test]
733    fn not_self_signed_revoked_userids() -> Result<()> {
734        let p = &StandardPolicy::new();
735
736        let alice_fpr: Fingerprint =
737            "D74DE22EDAE82EB8AD87A2D9816B56FDF9022248"
738           .parse().expect("valid fingerprint");
739        let alice_uid
740            = UserID::from("<alice@example.org>");
741
742        let bob_fpr: Fingerprint =
743            "3CC2F380C997730543AFCA3830887147FDECD335"
744           .parse().expect("valid fingerprint");
745        let bob_uid
746            = UserID::from("<bob@example.org>");
747        let bob_other_org_uid
748            = UserID::from("<bob@other.org>");
749        // Certified by: signer's cert not found
750
751        // $ date '+%s' -d 20200202
752        // 1580598000
753        let t1 = time::UNIX_EPOCH + time::Duration::new(1580598000, 0);
754        // $ date '+%s' -d 20200302
755        // 1583103600
756        let t2 = time::UNIX_EPOCH + time::Duration::new(1583103600, 0);
757
758        let certs: Vec<Cert> = CertParser::from_bytes(
759            &crate::testdata::data("userid-revoked-2.pgp"))?
760            .map(|c| c.expect("Valid certificate"))
761            .collect();
762
763        let store = CertStore::from_cert_refs(
764            certs.iter().map(|c| c.into()), p, t1)?;
765        let n = NetworkBuilder::rooted(store,
766            Roots::from(slice::from_ref(&alice_fpr)))
767            .build();
768
769        eprintln!("{:?}", n);
770
771        // bob@other.org is certified by alice.
772        let paths = n.authenticate(&bob_other_org_uid, bob_fpr.clone(),
773            FULLY_TRUSTED);
774        assert_eq!(paths.len(), 1);
775
776        let store = CertStore::from_cert_refs(
777            certs.iter().map(|c| c.into()), p, t2)?;
778        let n = NetworkBuilder::rooted(store,
779            Roots::from(slice::from_ref(&alice_fpr)))
780            .build();
781
782        // bob@other.org is certified by alice, but Bob has now
783        // revoked it.  This binding may not authenticate (no paths
784        // found).
785        let paths = n.authenticate(&bob_other_org_uid, bob_fpr.clone(),
786            FULLY_TRUSTED);
787        assert_eq!(paths.len(), 0);
788
789        Ok(())
790    }
791}