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