1
//! consensus documents - items that vary by consensus flavor
2
//!
3
//! **This file is reincluded multiple times**,
4
//! by the macros in [`crate::doc::ns_variety_definition_macros`],
5
//! once for votes, and once for each consensus flavour.
6
//! It is *not* a module `crate::doc::netstatus::rs::each_flavor`.
7
//!
8
//! Each time this file is included by one of the macros mentioned above,
9
//! the `ns_***` macros (such as `ns_const_name!`) may expand to different values.
10
//!
11
//! See [`crate::doc::ns_variety_definition_macros`].
12

            
13
use super::*;
14

            
15
ns_use_this_variety! {
16
    use [crate::doc::netstatus::rs]::?::{RouterStatus};
17
}
18
#[cfg(feature = "build_docs")]
19
ns_use_this_variety! {
20
    pub(crate) use [crate::doc::netstatus::build]::?::{ConsensusBuilder};
21
    pub use [crate::doc::netstatus::rs::build]::?::{RouterStatusBuilder};
22
}
23

            
24
/// A single consensus netstatus, as produced by the old parser.
25
#[derive(Debug, Clone)]
26
#[non_exhaustive]
27
pub struct Consensus {
28
    /// What kind of consensus document is this?  Absent in votes and
29
    /// in ns-flavored consensuses.
30
    pub flavor: ConsensusFlavor,
31
    /// The preamble, except for the intro item.
32
    pub preamble: Preamble,
33
    /// List of voters whose votes contributed to this consensus.
34
    pub voters: Vec<ConsensusVoterInfo>,
35
    /// A list of routerstatus entries for the relays on the network,
36
    /// with one entry per relay.
37
    ///
38
    /// These are currently ordered by the router's RSA identity, but this is not
39
    /// to be relied on, since we may want to even abolish RSA at some point!
40
    pub relays: Vec<RouterStatus>,
41
    /// Footer for the consensus object.
42
    pub footer: Footer,
43
}
44

            
45
impl Consensus {
46
    /// Return the Lifetime for this consensus.
47
87264
    pub fn lifetime(&self) -> &Lifetime {
48
87264
        &self.preamble.lifetime
49
87264
    }
50

            
51
    /// Return a slice of all the routerstatus entries in this consensus.
52
66624916
    pub fn relays(&self) -> &[RouterStatus] {
53
66624916
        &self.relays[..]
54
66624916
    }
55

            
56
    /// Return a mapping from keywords to integers representing how
57
    /// to weight different kinds of relays in different path positions.
58
10944
    pub fn bandwidth_weights(&self) -> &NetParams<i32> {
59
10944
        &self.footer.weights
60
10944
    }
61

            
62
    /// Return the map of network parameters that this consensus advertises.
63
10992
    pub fn params(&self) -> &NetParams<i32> {
64
10992
        &self.preamble.params
65
10992
    }
66

            
67
    /// Return the latest shared random value, if the consensus
68
    /// contains one.
69
43440
    pub fn shared_rand_cur(&self) -> Option<&SharedRandStatus> {
70
43440
        self.preamble.shared_rand_current_value.as_ref()
71
43440
    }
72

            
73
    /// Return the previous shared random value, if the consensus
74
    /// contains one.
75
43440
    pub fn shared_rand_prev(&self) -> Option<&SharedRandStatus> {
76
43440
        self.preamble.shared_rand_previous_value.as_ref()
77
43440
    }
78

            
79
    /// Return a [`ProtoStatus`] that lists the network's current requirements and
80
    /// recommendations for the list of protocols that every relay must implement.  
81
144
    pub fn relay_protocol_status(&self) -> &ProtoStatus {
82
144
        &self.preamble.proto_statuses.relay
83
144
    }
84

            
85
    /// Return a [`ProtoStatus`] that lists the network's current requirements and
86
    /// recommendations for the list of protocols that every client must implement.
87
    pub fn client_protocol_status(&self) -> &ProtoStatus {
88
        &self.preamble.proto_statuses.client
89
    }
90

            
91
    /// Return a set of all known [`ProtoStatus`] values.
92
48
    pub fn protocol_statuses(&self) -> &Arc<ProtoStatuses> {
93
48
        &self.preamble.proto_statuses
94
48
    }
95
}
96

            
97
impl Consensus {
98
    /// Return a new ConsensusBuilder for building test consensus objects.
99
    ///
100
    /// This function is only available when the `build_docs` feature has
101
    /// been enabled.
102
    #[cfg(feature = "build_docs")]
103
13204
    pub fn builder() -> ConsensusBuilder {
104
13204
        ConsensusBuilder::new(RouterStatus::flavor())
105
13204
    }
106

            
107
    /// Try to parse a single networkstatus document from a string.
108
402
    pub fn parse(s: &str) -> Result<(&str, &str, UncheckedConsensus)> {
109
402
        let mut reader = NetDocReader::new(s)?;
110
411
        Self::parse_from_reader(&mut reader).map_err(|e| e.within(s))
111
402
    }
112
    /// Extract a voter-info section from the reader; return
113
    /// Ok(None) when we are out of voter-info sections.
114
1402
    fn take_voterinfo(
115
1402
        r: &mut NetDocReader<'_, NetstatusKwd>,
116
1402
    ) -> Result<Option<ConsensusVoterInfo>> {
117
        use NetstatusKwd::*;
118

            
119
1402
        match r.peek() {
120
            None => return Ok(None),
121
1402
            Some(e) if e.is_ok_with_kwd_in(&[RS_R, DIRECTORY_FOOTER]) => return Ok(None),
122
1052
            _ => (),
123
        };
124

            
125
1052
        let mut first_dir_source = true;
126
        // TODO: Extract this pattern into a "pause at second"???
127
        // Pause at the first 'r', or the second 'dir-source'.
128
4335
        let mut p = r.pause_at(|i| match i {
129
            Err(_) => false,
130
4292
            Ok(item) => {
131
4292
                item.kwd() == RS_R
132
3935
                    || if item.kwd() == DIR_SOURCE {
133
1789
                        let was_first = first_dir_source;
134
1789
                        first_dir_source = false;
135
1789
                        !was_first
136
                    } else {
137
2146
                        false
138
                    }
139
            }
140
4292
        });
141

            
142
1052
        let voter_sec = NS_VOTERINFO_RULES_CONSENSUS.parse(&mut p)?;
143
1052
        let voter = ConsensusVoterInfo::from_section(&voter_sec)?;
144

            
145
1052
        Ok(Some(voter))
146
1402
    }
147

            
148
    /// Extract the footer (but not signatures) from the reader.
149
342
    fn take_footer(r: &mut NetDocReader<'_, NetstatusKwd>) -> Result<Footer> {
150
        use NetstatusKwd::*;
151
1057
        let mut p = r.pause_at(|i| i.is_ok_with_kwd_in(&[DIRECTORY_SIGNATURE]));
152
342
        let footer_sec = NS_FOOTER_RULES.parse(&mut p)?;
153
342
        let footer = Footer::from_section(&footer_sec)?;
154
340
        Ok(footer)
155
342
    }
156

            
157
    /// Extract a routerstatus from the reader.  Return Ok(None) if we're
158
    /// out of routerstatus entries.
159
2228
    fn take_routerstatus(r: &mut NetDocReader<'_, NetstatusKwd>) -> Result<Option<(Pos, RouterStatus)>> {
160
        use NetstatusKwd::*;
161
2228
        match r.peek() {
162
            None => return Ok(None),
163
2228
            Some(e) if e.is_ok_with_kwd_in(&[DIRECTORY_FOOTER]) => return Ok(None),
164
1886
            _ => (),
165
        };
166

            
167
1886
        let pos = r.pos();
168

            
169
1886
        let mut first_r = true;
170
15057
        let mut p = r.pause_at(|i| match i {
171
            Err(_) => false,
172
14988
            Ok(item) => {
173
14988
                item.kwd() == DIRECTORY_FOOTER
174
14637
                    || if item.kwd() == RS_R {
175
3497
                        let was_first = first_r;
176
3497
                        first_r = false;
177
3497
                        !was_first
178
                    } else {
179
11140
                        false
180
                    }
181
            }
182
14988
        });
183

            
184
1886
        let rules = match RouterStatus::flavor() {
185
1874
            ConsensusFlavor::Microdesc => &NS_ROUTERSTATUS_RULES_MDCON,
186
12
            ConsensusFlavor::Plain => &NS_ROUTERSTATUS_RULES_PLAIN,
187
        };
188

            
189
1886
        let rs_sec = rules.parse(&mut p)?;
190
1886
        let rs = RouterStatus::from_section(&rs_sec)?;
191
1880
        Ok(Some((pos, rs)))
192
2228
    }
193

            
194
    /// Extract an entire UncheckedConsensus from a reader.
195
    ///
196
    /// Returns the signed portion of the string, the remainder of the
197
    /// string, and an UncheckedConsensus.
198
402
    fn parse_from_reader<'a>(
199
402
        r: &mut NetDocReader<'a, NetstatusKwd>,
200
402
    ) -> Result<(&'a str, &'a str, UncheckedConsensus)> {
201
        use NetstatusKwd::*;
202
350
        let ((flavor, preamble), start_pos) = {
203
5483
            let mut h = r.pause_at(|i| i.is_ok_with_kwd_in(&[DIR_SOURCE]));
204
402
            let preamble_sec = NS_HEADER_RULES_CONSENSUS.parse(&mut h)?;
205
            // Unwrapping should be safe because above `.parse` would have
206
            // returned an Error
207
            #[allow(clippy::unwrap_used)]
208
354
            let pos = preamble_sec.first_item().unwrap().offset_in(r.str()).unwrap();
209
354
            (Preamble::from_section(&preamble_sec)?, pos)
210
        };
211
350
        if RouterStatus::flavor() != flavor {
212
            return Err(EK::BadDocumentType.with_msg(format!(
213
                "Expected {:?}, got {:?}",
214
                RouterStatus::flavor(),
215
                flavor
216
            )));
217
350
        }
218

            
219
350
        let mut voters = Vec::new();
220

            
221
1402
        while let Some(voter) = Self::take_voterinfo(r)? {
222
1052
            voters.push(voter);
223
1052
        }
224

            
225
350
        let mut relays: Vec<RouterStatus> = Vec::new();
226
2228
        while let Some((pos, routerstatus)) = Self::take_routerstatus(r)? {
227
1880
            if let Some(prev) = relays.last() {
228
1532
                if prev.rsa_identity() >= routerstatus.rsa_identity() {
229
2
                    return Err(EK::WrongSortOrder.at_pos(pos));
230
1530
                }
231
348
            }
232
1878
            relays.push(routerstatus);
233
        }
234
342
        relays.shrink_to_fit();
235

            
236
342
        let footer = Self::take_footer(r)?;
237

            
238
340
        let consensus = Consensus {
239
340
            flavor,
240
340
            preamble,
241
340
            voters,
242
340
            relays,
243
340
            footer,
244
340
        };
245

            
246
        // Find the signatures.
247
340
        let mut first_sig: Option<Item<'_, NetstatusKwd>> = None;
248
340
        let mut signatures = Vec::new();
249
1362
        for item in &mut *r {
250
1022
            let item = item?;
251
1022
            if item.kwd() != DIRECTORY_SIGNATURE {
252
                return Err(EK::UnexpectedToken
253
                    .with_msg(item.kwd().to_str())
254
                    .at_pos(item.pos()));
255
1022
            }
256

            
257
1022
            let sig = Signature::from_item(&item)?;
258
1022
            if first_sig.is_none() {
259
340
                first_sig = Some(item);
260
682
            }
261
1022
            signatures.push(sig);
262
        }
263

            
264
340
        let end_pos = match first_sig {
265
            None => return Err(EK::MissingToken.with_msg("directory-signature")),
266
            // Unwrap should be safe because `first_sig` was parsed from `r`
267
            #[allow(clippy::unwrap_used)]
268
340
            Some(sig) => sig.offset_in(r.str()).unwrap() + "directory-signature ".len(),
269
        };
270

            
271
        // Find the appropriate digest.
272
340
        let signed_str = &r.str()[start_pos..end_pos];
273
340
        let remainder = &r.str()[end_pos..];
274
340
        let (sha256, sha1) = match RouterStatus::flavor() {
275
2
            ConsensusFlavor::Plain => (
276
2
                None,
277
2
                Some(ll::d::Sha1::digest(signed_str.as_bytes()).into()),
278
2
            ),
279
338
            ConsensusFlavor::Microdesc => (
280
338
                Some(ll::d::Sha256::digest(signed_str.as_bytes()).into()),
281
338
                None,
282
338
            ),
283
        };
284
340
        let siggroup = SignatureGroup {
285
340
            sha256,
286
340
            sha1,
287
340
            signatures,
288
340
        };
289

            
290
340
        let unval = UnvalidatedConsensus {
291
340
            consensus,
292
340
            siggroup,
293
340
            n_authorities: None,
294
340
        };
295
340
        let lifetime = unval.consensus.preamble.lifetime.clone();
296
340
        let delay = unval.consensus.preamble.voting_delay.unwrap_or((0, 0));
297
340
        let dist_interval = time::Duration::from_secs(delay.1.into());
298
340
        let starting_time = *lifetime.valid_after - dist_interval;
299
340
        let timebound = TimerangeBound::new(unval, starting_time..*lifetime.valid_until);
300
340
        Ok((signed_str, remainder, timebound))
301
402
    }
302
}
303

            
304
impl Preamble {
305
    /// Extract the CommonPreamble members from a single preamble section.
306
354
    fn from_section(sec: &Section<'_, NetstatusKwd>) -> Result<(ConsensusFlavor, Preamble)> {
307
        use NetstatusKwd::*;
308

            
309
        {
310
            // this unwrap is safe because if there is not at least one
311
            // token in the section, the section is unparsable.
312
            #[allow(clippy::unwrap_used)]
313
354
            let first = sec.first_item().unwrap();
314
354
            if first.kwd() != NETWORK_STATUS_VERSION {
315
2
                return Err(EK::UnexpectedToken
316
2
                    .with_msg(first.kwd().to_str())
317
2
                    .at_pos(first.pos()));
318
352
            }
319
        }
320

            
321
352
        let ver_item = sec.required(NETWORK_STATUS_VERSION)?;
322

            
323
352
        let version: u32 = ver_item.parse_arg(0)?;
324
352
        if version != 3 {
325
2
            return Err(EK::BadDocumentVersion.with_msg(version.to_string()));
326
350
        }
327
350
        let flavor = ConsensusFlavor::from_opt_name(ver_item.arg(1))?;
328

            
329
350
        let valid_after = sec
330
350
            .required(VALID_AFTER)?
331
350
            .args_as_str()
332
350
            .parse::<Iso8601TimeSp>()?
333
350
            .into();
334
350
        let fresh_until = sec
335
350
            .required(FRESH_UNTIL)?
336
350
            .args_as_str()
337
350
            .parse::<Iso8601TimeSp>()?
338
350
            .into();
339
350
        let valid_until = sec
340
350
            .required(VALID_UNTIL)?
341
350
            .args_as_str()
342
350
            .parse::<Iso8601TimeSp>()?
343
350
            .into();
344
350
        let lifetime = Lifetime::new(valid_after, fresh_until, valid_until)?;
345

            
346
350
        let client_versions = sec
347
350
            .maybe(CLIENT_VERSIONS)
348
350
            .args_as_str()
349
350
            .unwrap_or("")
350
350
            .split(',')
351
350
            .map(str::to_string)
352
350
            .collect();
353
350
        let server_versions = sec
354
350
            .maybe(SERVER_VERSIONS)
355
350
            .args_as_str()
356
350
            .unwrap_or("")
357
350
            .split(',')
358
350
            .map(str::to_string)
359
350
            .collect();
360

            
361
350
        let proto_statuses = {
362
350
            let client = ProtoStatus::from_section(
363
350
                sec,
364
350
                RECOMMENDED_CLIENT_PROTOCOLS,
365
350
                REQUIRED_CLIENT_PROTOCOLS,
366
            )?;
367
350
            let relay = ProtoStatus::from_section(
368
350
                sec,
369
350
                RECOMMENDED_RELAY_PROTOCOLS,
370
350
                REQUIRED_RELAY_PROTOCOLS,
371
            )?;
372
350
            Arc::new(ProtoStatuses { client, relay })
373
        };
374

            
375
350
        let params = sec.maybe(PARAMS).args_as_str().unwrap_or("").parse()?;
376

            
377
350
        let status: &str = sec.required(VOTE_STATUS)?.arg(0).unwrap_or("");
378
350
        if status != "consensus" {
379
            return Err(EK::BadDocumentType.err());
380
350
        }
381

            
382
        // We're ignoring KNOWN_FLAGS in the consensus.
383

            
384
350
        let consensus_method: u32 = sec.required(CONSENSUS_METHOD)?.parse_arg(0)?;
385

            
386
350
        let shared_rand_previous_value = sec
387
350
            .get(SHARED_RAND_PREVIOUS_VALUE)
388
350
            .map(SharedRandStatus::from_item)
389
350
            .transpose()?;
390

            
391
350
        let shared_rand_current_value = sec
392
350
            .get(SHARED_RAND_CURRENT_VALUE)
393
350
            .map(SharedRandStatus::from_item)
394
350
            .transpose()?;
395

            
396
350
        let voting_delay = if let Some(tok) = sec.get(VOTING_DELAY) {
397
350
            let n1 = tok.parse_arg(0)?;
398
350
            let n2 = tok.parse_arg(1)?;
399
350
            Some((n1, n2))
400
        } else {
401
            None
402
        };
403

            
404
350
        let preamble = Preamble {
405
350
            lifetime,
406
350
            client_versions,
407
350
            server_versions,
408
350
            proto_statuses,
409
350
            params,
410
350
            voting_delay,
411
350
            consensus_method,
412
350
            published: NotPresent,
413
350
            consensus_methods: NotPresent,
414
350
            shared_rand_previous_value,
415
350
            shared_rand_current_value,
416
350
        };
417

            
418
350
        Ok((flavor, preamble))
419
354
    }
420
}
421

            
422
/// A Microdesc consensus whose signatures have not yet been checked.
423
///
424
/// To validate this object, call set_n_authorities() on it, then call
425
/// check_signature() on that result with the set of certs that you
426
/// have.  Make sure only to provide authority certificates representing
427
/// real authorities!
428
#[derive(Debug, Clone)]
429
#[non_exhaustive]
430
pub struct UnvalidatedConsensus {
431
    /// The consensus object. We don't want to expose this until it's
432
    /// validated.
433
    pub consensus: Consensus,
434
    /// The signatures that need to be validated before we can call
435
    /// this consensus valid.
436
    pub siggroup: SignatureGroup,
437
    /// The total number of authorities that we believe in.  We need
438
    /// this information in order to validate the signatures, since it
439
    /// determines how many signatures we need to find valid in `siggroup`.
440
    pub n_authorities: Option<usize>,
441
}
442

            
443
impl UnvalidatedConsensus {
444
    /// Tell the unvalidated consensus how many authorities we believe in.
445
    ///
446
    /// Without knowing this number, we can't validate the signature.
447
    #[must_use]
448
244
    pub fn set_n_authorities(self, n_authorities: usize) -> Self {
449
244
        UnvalidatedConsensus {
450
244
            n_authorities: Some(n_authorities),
451
244
            ..self
452
244
        }
453
244
    }
454

            
455
    /// Return an iterator of all the certificate IDs that we might use
456
    /// to validate this consensus.
457
192
    pub fn signing_cert_ids(&self) -> impl Iterator<Item = AuthCertKeyIds> {
458
192
        match self.key_is_correct(&[]) {
459
            Ok(()) => Vec::new(),
460
192
            Err(missing) => missing,
461
        }
462
192
        .into_iter()
463
192
    }
464

            
465
    /// Return the lifetime of this unvalidated consensus
466
240
    pub fn peek_lifetime(&self) -> &Lifetime {
467
240
        self.consensus.lifetime()
468
240
    }
469

            
470
    /// Return true if a client who believes in exactly the provided
471
    /// set of authority IDs might might consider this consensus to be
472
    /// well-signed.
473
    ///
474
    /// (This is the case if the consensus claims to be signed by more than
475
    /// half of the authorities in the list.)
476
250
    pub fn authorities_are_correct(&self, authorities: &[&RsaIdentity]) -> bool {
477
250
        self.siggroup.could_validate(authorities)
478
250
    }
479

            
480
    /// Return the number of relays in this unvalidated consensus.
481
    ///
482
    /// This function is unstable. It is only enabled if the crate was
483
    /// built with the `experimental-api` feature.
484
    #[cfg(feature = "experimental-api")]
485
    pub fn n_relays(&self) -> usize {
486
        self.consensus.relays.len()
487
    }
488

            
489
    /// Modify the list of relays in this unvalidated consensus.
490
    ///
491
    /// A use case for this is long-lasting custom directories. To ensure Arti can still quickly
492
    /// build circuits when the directory gets old, a tiny churn file can be regularly obtained,
493
    /// listing no longer available Tor nodes, which can then be removed from the consensus.
494
    ///
495
    /// This function is unstable. It is only enabled if the crate was
496
    /// built with the `experimental-api` feature.
497
    #[cfg(feature = "experimental-api")]
498
    pub fn modify_relays<F>(&mut self, func: F)
499
    where
500
        F: FnOnce(&mut Vec<RouterStatus>),
501
    {
502
        func(&mut self.consensus.relays);
503
    }
504
}
505

            
506
impl ExternallySigned<Consensus> for UnvalidatedConsensus {
507
    type Key = [AuthCert];
508
    type KeyHint = Vec<AuthCertKeyIds>;
509
    type Error = Error;
510

            
511
300
    fn key_is_correct(&self, k: &Self::Key) -> result::Result<(), Self::KeyHint> {
512
300
        let (n_ok, missing) = self.siggroup.list_missing(k);
513
300
        match self.n_authorities {
514
300
            Some(n) if n_ok > (n / 2) => Ok(()),
515
246
            _ => Err(missing.iter().map(|cert| cert.key_ids).collect()),
516
        }
517
300
    }
518
54
    fn is_well_signed(&self, k: &Self::Key) -> result::Result<(), Self::Error> {
519
54
        match self.n_authorities {
520
            None => Err(Error::from(internal!(
521
                "Didn't set authorities on consensus"
522
            ))),
523
54
            Some(authority) => {
524
54
                if self.siggroup.validate(authority, k) {
525
52
                    Ok(())
526
                } else {
527
2
                    Err(EK::BadSignature.err())
528
                }
529
            }
530
        }
531
54
    }
532
148
    fn dangerously_assume_wellsigned(self) -> Consensus {
533
148
        self.consensus
534
148
    }
535
}
536

            
537
/// A Consensus object that has been parsed, but not checked for
538
/// signatures and timeliness.
539
pub type UncheckedConsensus = TimerangeBound<UnvalidatedConsensus>;
540