1
//!
2
//! A "router descriptor" is a signed statement that a relay makes
3
//! about itself, explaining its keys, its capabilities, its location,
4
//! and its status.
5
//!
6
//! Relays upload their router descriptors to authorities, which use
7
//! them to build consensus documents.  Old clients and relays used to
8
//! fetch and use router descriptors for all the relays, but nowadays they use
9
//! microdescriptors instead.
10
//!
11
//! Clients still use router descriptors when communicating with
12
//! bridges: since bridges are not passed through an authority,
13
//! clients accept their descriptors directly.
14
//!
15
//! For full information about the router descriptor format, see
16
//! [dir-spec.txt](https://spec.torproject.org/dir-spec).
17
//!
18
//! # Limitations
19
//!
20
//! TODO: This needs to get tested much more!
21
//!
22
//! TODO: This implementation can be memory-inefficient.  In practice,
23
//! it gets really expensive storing policy entries, family
24
//! descriptions, parsed keys, and things like that.  We will probably want to
25
//! de-duplicate those.
26
//!
27
//! TODO: There should be accessor functions for some or all of the
28
//! fields in RouterDesc.  I'm deferring those until I know what they
29
//! should be.
30
use crate::encode::{ItemEncoder, ItemValueEncodable, NetdocEncodable, NetdocEncoder};
31
use crate::parse::keyword::Keyword;
32
use crate::parse::parser::{Section, SectionRules};
33
use crate::parse::tokenize::{ItemResult, NetDocReader};
34
use crate::parse2::{
35
    ArgumentError, ErrorProblem, ItemValueParseable, SignaturesData, UnparsedItem, VerifyFailed,
36
};
37
use crate::types::descriptor::*;
38
use crate::types::family::{RelayFamily, RelayFamilyIds};
39
use crate::types::policy::*;
40
use crate::types::version::TorVersion;
41
use crate::types::{EmbeddedCert, misc::*};
42
use crate::util::PeekableIterator;
43
use crate::{
44
    AllowAnnotations, Error, KeywordEncodable, NetdocErrorKind as EK, NormalItemArgument, Result,
45
};
46

            
47
use derive_deftly::Deftly;
48
use ll::pk::ed25519::Ed25519Identity;
49
use saturating_time::SaturatingTime;
50
use std::fmt::Display;
51
use std::sync::LazyLock;
52
use std::{iter, net, time};
53
use tor_basic_utils::intern::Intern;
54
use tor_cert::{CertType, KeyUnknownCert};
55
use tor_checkable::timed::{TimeRangeBound, TimeRangeBoundBuilder};
56
use tor_checkable::{Timebound, signed, timed};
57
use tor_error::{internal, into_internal};
58
use tor_llcrypto as ll;
59
use tor_llcrypto::pk::ed25519::{self, Ed25519PublicKey};
60
use tor_llcrypto::pk::keymanip::convert_curve25519_to_ed25519_public;
61
use tor_llcrypto::pk::rsa::{self, RsaIdentity};
62

            
63
use digest::Digest;
64

            
65
/// Length of a router descriptor digest
66
pub const DOC_DIGEST_LEN: usize = 20;
67

            
68
/// The digest of a RouterDesc document, as reported in a NS consensus.
69
pub type RdDigest = [u8; DOC_DIGEST_LEN];
70

            
71
/// The digest of an ExtraInfo document, as reported in a RouterDesc.
72
pub type ExtraInfoDigest = [u8; DOC_DIGEST_LEN];
73

            
74
/// A router descriptor, with possible annotations.
75
#[non_exhaustive]
76
pub struct AnnotatedRouterDesc {
77
    /// Annotation for this router descriptor; possibly empty.
78
    pub ann: RouterAnnotation,
79
    /// Underlying router descriptor; signatures not checked yet.
80
    pub router: UncheckedRouterDesc,
81
}
82

            
83
/// Annotations about a router descriptor, as stored on disc.
84
#[derive(Default)]
85
#[non_exhaustive]
86
pub struct RouterAnnotation {
87
    /// Description of where we got this router descriptor
88
    pub source: Option<String>,
89
    /// When this descriptor was first downloaded.
90
    pub downloaded: Option<time::SystemTime>,
91
    /// Description of what we're willing to use this descriptor for.
92
    pub purpose: Option<String>,
93
}
94

            
95
/// Information about a relay, parsed from a router descriptor.
96
///
97
/// This type does not hold all the information in the router descriptor
98
///
99
/// # Limitations
100
///
101
/// See module documentation.
102
///
103
/// Additionally, some fields that from router descriptors are not yet
104
/// parsed: see the comments in ROUTER_BODY_RULES for information about those.
105
///
106
/// Before using this type to connect to a relay, you MUST check that
107
/// it is valid, using is_expired_at().
108
///
109
/// # Specification
110
///
111
/// <https://spec.torproject.org/dir-spec/server-descriptor-format.html>
112
#[derive(Clone, Debug, Deftly, PartialEq)]
113
#[derive_deftly(Constructor)]
114
#[derive_deftly(NetdocParseableUnverified, NetdocEncodable)]
115
#[allow(clippy::exhaustive_structs)]
116
pub struct RouterDesc {
117
    /// `router` --- Introduce a router descriptor.
118
    ///
119
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:router>
120
    #[deftly(constructor)]
121
    pub router: RouterDescIntroItem,
122

            
123
    /// `identity-ed25519` --- Specify the router's ed25519 identity.
124
    ///
125
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:identity-ed25519>
126
    #[deftly(constructor)]
127
    pub identity_ed25519: EmbeddedCert<Ed25519IdentityCert, KeyUnknownCert>,
128

            
129
    /// `master-key-ed25519` --- Redundantly specify the router's ed25519 identity.
130
    ///
131
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:master-key-ed25519>
132
    #[deftly(netdoc(single_arg))]
133
    #[deftly(constructor)]
134
    pub master_key_ed25519: Ed25519Public,
135

            
136
    /// `bandwidth` --- Report router's network bandwidth.
137
    ///
138
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:bandwidth>
139
    pub bandwidth: Bandwidth,
140

            
141
    /// `platform` --- Describe the platform on which this relay is running.
142
    ///
143
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:platform>
144
    pub platform: Option<RelayPlatform>,
145

            
146
    /// `published` --- Time this descriptor (and extra-info) was generated.
147
    ///
148
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:published>
149
    #[deftly(netdoc(single_arg))]
150
    #[deftly(constructor)]
151
    pub published: Iso8601TimeSp,
152

            
153
    /// `fingerprint` --- Redundant hash of ASN-1 encoding of router identity key.
154
    ///
155
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:fingerprint>
156
    #[deftly(netdoc(single_arg))]
157
    pub fingerprint: Option<SpFingerprint>,
158

            
159
    /// `hibernating` --- Whether the relay is hibernating.
160
    ///
161
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:hibernating>
162
    #[deftly(netdoc(single_arg, default(skip)))]
163
    pub hibernating: NumericBoolean,
164

            
165
    /// `uptime` --- How long this relay has been continously running
166
    ///
167
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:uptime>
168
    #[deftly(netdoc(single_arg))]
169
    pub uptime: Option<u64>,
170

            
171
    /// `ntor-onion-key` --- The circuit extension key.
172
    ///
173
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:ntor-onion-key>
174
    #[deftly(netdoc(single_arg))]
175
    #[deftly(constructor)]
176
    pub ntor_onion_key: Curve25519Public,
177

            
178
    /// `ntor-onion-key-crosscert` --- Reverse cert by K_ntor on KP_relayid_ed
179
    ///
180
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:ntor-onion-key-crosscert>
181
    #[deftly(constructor)]
182
    pub ntor_onion_key_crosscert: NtorOnionKeyCrossCert,
183

            
184
    /// `signing-key` --- Obsolete RSA identity key.
185
    ///
186
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:signing-key>
187
    #[deftly(constructor)]
188
    pub signing_key: ll::pk::rsa::PublicKey,
189

            
190
    /// `accept, reject` --- Exit policy.
191
    ///
192
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:accept>
193
    // TODO: these polices can get bulky too. Perhaps we should
194
    // de-duplicate them too.
195
    // Not skipping the default here is probably desirable, as this field should
196
    // generally always be ended with a default policy (i.e. default accept,
197
    // default deny).
198
    #[deftly(netdoc(flatten))]
199
    pub ipv4_policy: AddrPolicy,
200

            
201
    /// `ipv6-policy` --- Exit plicy summary for IPv6
202
    ///
203
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:ipv6-policy>
204
    #[deftly(netdoc(default(skip)))]
205
    pub ipv6_policy: Intern<PortPolicy>,
206

            
207
    /// `overload-general` --- Relay is overloaded.
208
    ///
209
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:overload-general>
210
    // TODO in OverloadGeneral use ConstantString (from !3985) for version
211
    pub overload_general: Option<OverloadGeneral>,
212

            
213
    /// `contact` --- Server administrator contact information.
214
    ///
215
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:contact>
216
    pub contact: Option<ContactInfo>,
217

            
218
    /// `family` --- Group relays for the purpose of path selection.
219
    ///
220
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:family>
221
    #[deftly(netdoc(default(skip)))]
222
    pub family: Intern<RelayFamily>,
223

            
224
    /// `family-cert` --- Prove membership in a relay family.
225
    ///
226
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:family-cert>
227
    pub family_cert: RetainedOrderVec<EmbeddedCert<Ed25519FamilyCert, KeyUnknownCert>>,
228

            
229
    /// `caches-extra-info` --- Router provides extra-info as a dirmirror.
230
    ///
231
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#caches-extra-info>
232
    pub caches_extra_info: Option<ItemPresent<CachesExtraInfoToken>>,
233

            
234
    /// `extra-info-digest` --- Hash of the extra-info document.
235
    ///
236
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:extra-info-digest>
237
    pub extra_info_digest: Option<ExtraInfoDigests>,
238

            
239
    /// `hidden-service-dir` --- Declares this router to be a hidden service directory
240
    ///
241
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:hidden-service-dir>
242
    pub hidden_service_dir: Option<ItemPresent<HiddenServiceDirToken>>,
243

            
244
    /// `or-address` --- Alternative ORport address and port
245
    ///
246
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:or-address>
247
    #[deftly(netdoc(single_arg))]
248
    pub or_address: Vec<net::SocketAddr>,
249

            
250
    /// `tunnelled-dir-server` --- Accepts a `BEGIN_DIR` relay message.
251
    ///
252
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#tunnelled-dir-server>
253
    pub tunnelled_dir_server: Option<ItemPresent<TunnelledDirServerToken>>,
254

            
255
    /// `proto` --- Subprotocol capabilities supported.
256
    ///
257
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:proto>
258
    pub proto: tor_protover::Protocols,
259

            
260
    #[doc(hidden)]
261
    #[deftly(netdoc(skip))]
262
    pub __non_exhaustive: (),
263
}
264

            
265
/// Signatures of a [`RouterDesc`].
266
///
267
/// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:router-sig-ed25519>
268
///
269
// TODO(relay): Would be grand to be able to use this within RouterDesc::encode_sign()
270
// but it would need to support "signature sections" I guess or something around those
271
// lines because the RSA signature covers the Ed25519 signature.
272
#[derive(Clone, Debug, PartialEq, Deftly)]
273
#[derive_deftly(NetdocParseableSignatures, NetdocEncodable)]
274
#[deftly(netdoc(signatures(hashes_accu = "RouterHashAccu")))]
275
#[non_exhaustive]
276
pub struct RouterDescSignatures {
277
    /// `router-sig-ed25519` --- Ed25519 signature
278
    ///
279
    /// Ed25519 signature by the Ed25519 signing key on the SHA-256 digest of
280
    /// the document prefixed by a magic up until and including the
281
    /// `router-sig-ed25519` keyword plus space.
282
    pub router_sig_ed25519: RouterSigEd25519,
283

            
284
    /// `router-signature` --- RSA signature
285
    ///
286
    /// * At end, exactly once.
287
    /// * RSA signature of the document, including `router-sig-ed25519`.
288
    pub router_signature: RouterSignature,
289
}
290

            
291
impl RouterDescUnverified {
292
    /// Verifies a self-signed [`RouterDescUnverified`].
293
    ///
294
    /// This verification performs the following checks:
295
    /// * [`RouterDesc::identity_ed25519`] is validly signed.
296
    /// * [`RouterDesc::master_key_ed25519`] is as implied by [`RouterDesc::identity_ed25519`].
297
    /// * [`RouterDesc::fingerprint`] is as implied by [`RouterDesc::signing_key`].
298
    /// * [`RouterDesc::ntor_onion_key_crosscert`] is validly signed.
299
    /// * [`RouterDesc::signing_key`] has correct length and exponent.
300
    /// * All [`RouterDesc::family_cert`] elements are valid.
301
    /// * The inner and outer [`RouterDescSignatures`] are valid.
302
    ///
303
    /// The result will be a [`TimeRangeBound`] composed from the respective
304
    /// minimums and maximums found in the present certificates.  For the lower
305
    /// bound, [`RouterDesc::published`] will also be taken into account when
306
    /// determining the minimum.
307
    //
308
    // We deny the use of unused variables as a hint to use all TimeRangeBound
309
    // values obtained through a dangerous split.
310
    #[deny(unused_variables)]
311
12505
    pub fn verify(self) -> std::result::Result<TimeRangeBound<RouterDesc>, VerifyFailed> {
312
12505
        let logic = |trbb: &mut TimeRangeBoundBuilder| -> _ {
313
            // Type annotations to make LSP happy.
314
12505
            let (mut body, sigs): (RouterDesc, SignaturesData<_>) = (self.body, self.sigs);
315

            
316
            // Verify the ed25519 identity certificate.
317
            // This also includes a check for the master-key-ed25519.
318
12505
            let identity_ed25519 =
319
12505
                Ed25519IdentityCert::verify(body.identity_ed25519.raw_unverified().clone())?
320
12505
                    .unwrap_with(trbb);
321
            let Ed25519IdentityCert {
322
12505
                id_ed25519,
323
12505
                sign_ed25519,
324
12505
            } = identity_ed25519;
325
12505
            if id_ed25519 != body.master_key_ed25519.0 {
326
1
                return Err(VerifyFailed::Inconsistent);
327
12504
            }
328
12504
            body.identity_ed25519.set_verified(identity_ed25519);
329

            
330
            // Keep track of the published value as lower time bound.
331
12504
            trbb.intersect_bounds(TimeRangeBound::new((), body.published.0..));
332

            
333
            // If set, ensure that the fingerprint equals to the signing key id.
334
12504
            if body
335
12504
                .fingerprint
336
12504
                .is_some_and(|fp| fp.0 != body.signing_key.to_rsa_identity())
337
            {
338
1
                return Err(VerifyFailed::Inconsistent);
339
12503
            }
340

            
341
            // Verify the ntor-onion-key-crosscert.
342
            // For this, we also need to convert the X25519 ntor key to an Ed25519
343
            // key using convert_curve25519_to_ed25519_public().
344
12503
            let ntor_pk = convert_curve25519_to_ed25519_public(
345
12503
                &body.ntor_onion_key.0,
346
                // Rust std turns false into 0 and true into 1.
347
12503
                body.ntor_onion_key_crosscert.bit.0.into(),
348
            )
349
12503
            .ok_or(VerifyFailed::Other)?;
350
12503
            let ntor_cc = Ed25519NtorCrossCert::verify(
351
12503
                ntor_pk.into(),
352
12503
                id_ed25519,
353
12503
                body.ntor_onion_key_crosscert.cert.raw_unverified().clone(),
354
1
            )?
355
12502
            .unwrap_with(trbb);
356
12502
            body.ntor_onion_key_crosscert.cert.set_verified(ntor_cc);
357

            
358
            // Verify that the signing key has the proper exponent and length.
359
            // TODO DIRAUTH: We want to enforce this type wise with parse2.
360
12502
            if body.signing_key.bits() != 1024 || !body.signing_key.exponent_is(65537) {
361
                return Err(VerifyFailed::Other);
362
12502
            }
363

            
364
            // Verify all family certificates.
365
12502
            for cert in body.family_cert.0.iter_mut() {
366
477
                let cert_verified =
367
477
                    Ed25519FamilyCert::verify(id_ed25519, cert.raw_unverified().clone())?
368
477
                        .unwrap_with(trbb);
369
477
                cert.set_verified(cert_verified);
370
            }
371

            
372
            // Verify the actual outer document signatures.
373
            // VerifyFailed should be an okay error variant in case that the hashes
374
            // were not accumulated, as it is not possible to verify without a
375
            // hash.
376
12502
            ed25519::PublicKey::try_from(sign_ed25519)
377
12502
                .map_err(|_| VerifyFailed::Other)?
378
12502
                .verify(
379
12502
                    &sigs.hashes.sha256.ok_or(VerifyFailed::VerifyFailed)?,
380
12502
                    &sigs.sigs.router_sig_ed25519.0,
381
                )?;
382
12502
            body.signing_key.verify(
383
12502
                sigs.hashes.sha1.ok_or(VerifyFailed::VerifyFailed)?.as_ref(),
384
12502
                sigs.sigs.router_signature.0.as_ref(),
385
            )?;
386

            
387
12502
            Ok(body)
388
12505
        };
389

            
390
12505
        TimeRangeBound::build_intersect(logic)
391
12505
    }
392
}
393

            
394
/// Description of the software a relay is running.
395
///
396
/// `platform` line in a routerstatus.
397
/// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:platform>
398
// TODO: Move this to types/misc.rs.
399
#[derive(Debug, Clone, PartialEq, Eq)]
400
#[non_exhaustive]
401
pub enum RelayPlatform {
402
    /// Software advertised to be some version of Tor, on some platform.
403
    Tor(TorVersion, Option<String>),
404
    /// Software not advertised to be Tor.
405
    Other(String),
406
}
407

            
408
/// Zero-sized token type for use in [`RouterDesc::caches_extra_info`].
409
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
410
#[non_exhaustive]
411
pub struct CachesExtraInfoToken;
412

            
413
/// Zero-sized token type for use in [`RouterDesc::hidden_service_dir`].
414
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
415
#[non_exhaustive]
416
pub struct HiddenServiceDirToken;
417

            
418
/// Zero-sized token type for use in [`RouterDesc::tunnelled_dir_server`].
419
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
420
#[non_exhaustive]
421
pub struct TunnelledDirServerToken;
422

            
423
impl std::str::FromStr for RelayPlatform {
424
    type Err = Error;
425
14809
    fn from_str(args: &str) -> Result<Self> {
426
14809
        if args.starts_with("Tor ") {
427
14808
            let v: Vec<_> = args.splitn(4, ' ').collect();
428
14808
            match &v[..] {
429
14805
                ["Tor", ver, "on", p] => {
430
14805
                    Ok(RelayPlatform::Tor(ver.parse()?, Some((*p).to_string())))
431
                }
432
3
                ["Tor", ver, ..] => Ok(RelayPlatform::Tor(ver.parse()?, None)),
433
                _ => unreachable!(),
434
            }
435
        } else {
436
1
            Ok(RelayPlatform::Other(args.to_string()))
437
        }
438
14809
    }
439
}
440

            
441
impl Display for RelayPlatform {
442
25
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
443
24
        match &self {
444
21
            Self::Tor(v, Some(p)) => write!(f, "Tor {v} on {p}"),
445
3
            Self::Tor(v, None) => write!(f, "Tor {v}"),
446
1
            Self::Other(s) => write!(f, "{s}"),
447
        }
448
25
    }
449
}
450

            
451
impl ItemValueParseable for RelayPlatform {
452
12573
    fn from_unparsed(item: UnparsedItem<'_>) -> std::result::Result<Self, ErrorProblem> {
453
12573
        let mut args = item.args_copy();
454
12573
        item.check_no_object()?;
455
12573
        args.into_remaining()
456
12573
            .parse()
457
12573
            .map_err(|_| args.handle_error("platform", ArgumentError::Invalid))
458
12573
    }
459
}
460

            
461
impl ItemValueEncodable for RelayPlatform {
462
20
    fn write_item_value_onto(
463
20
        &self,
464
20
        mut out: ItemEncoder,
465
20
    ) -> std::result::Result<(), tor_error::Bug> {
466
        // Adding a raw string is fine because this is effectively a free form
467
        // field.
468
20
        out.args_raw_string(&self);
469
20
        Ok(())
470
20
    }
471
}
472

            
473
/// Version argument found in an `overload-general` item.
474
///
475
/// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:overload-general>
476
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, strum::EnumString, strum::Display)]
477
#[non_exhaustive]
478
pub enum OverloadGeneralVersion {
479
    /// Version 1, currently the only supported and specified one.
480
    #[strum(serialize = "1")]
481
    V1,
482
}
483

            
484
impl NormalItemArgument for OverloadGeneralVersion {}
485

            
486
/// The overload general type found in router descriptors.
487
///
488
/// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:overload-general>
489
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deftly)]
490
#[derive_deftly(Constructor, ItemValueParseable, ItemValueEncodable)]
491
#[allow(clippy::exhaustive_structs)]
492
pub struct OverloadGeneral {
493
    /// The version of the item.
494
    #[deftly(constructor)]
495
    pub version: OverloadGeneralVersion,
496
    /// The timestamp since when the relay is overloaded.
497
    #[deftly(constructor)]
498
    pub since: Iso8601TimeSp,
499

            
500
    #[doc(hidden)]
501
    #[deftly(netdoc(skip))]
502
    pub __non_exhaustive: (),
503
}
504

            
505
/// Introduction line of a router descriptor.
506
///
507
/// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:router>
508
#[derive(Clone, Debug, PartialEq, Eq, Deftly)]
509
#[derive_deftly(Constructor, ItemValueParseable, ItemValueEncodable)]
510
#[allow(clippy::exhaustive_structs)]
511
pub struct RouterDescIntroItem {
512
    /// A valid router [`Nickname`].
513
    #[deftly(constructor)]
514
    pub nickname: Nickname,
515

            
516
    /// An IPv4 address in dotted-squad format.
517
    #[deftly(constructor)]
518
    pub address: std::net::Ipv4Addr,
519

            
520
    /// The TCP port of the onion router.
521
    #[deftly(constructor)]
522
    pub orport: u16,
523

            
524
    /// Legacy.
525
    pub socksport: u16,
526

            
527
    /// Legacy.
528
    pub dirport: u16,
529

            
530
    #[doc(hidden)]
531
    #[deftly(netdoc(skip))]
532
    pub __non_exhaustive: (),
533
}
534

            
535
/// Digest identifying the extra-info document.
536
///
537
/// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:extra-info-digest>
538
#[derive(Clone, Debug, PartialEq, Eq, Deftly)]
539
#[derive_deftly(Constructor, ItemValueParseable, ItemValueEncodable)]
540
#[allow(clippy::exhaustive_structs)]
541
pub struct ExtraInfoDigests {
542
    /// Mandatory SHA-1 of the signed data in base 16.
543
    #[deftly(constructor)]
544
    pub sha1: FixedB16U<20>,
545

            
546
    /// Optional SHA-256 of the entire extra-info in base 64.
547
    pub sha2: Option<FixedB64<32>>,
548

            
549
    #[doc(hidden)]
550
    #[deftly(netdoc(skip))]
551
    pub __non_exhaustive: (),
552
}
553

            
554
/// Estimated bandwidth for a router.
555
///
556
/// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:bandwidth>
557
// Does not derive Ord because it only makes sense to order on a single
558
// field but not all.
559
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Deftly)]
560
#[derive_deftly(Constructor, ItemValueParseable, ItemValueEncodable)]
561
#[allow(clippy::exhaustive_structs)]
562
pub struct Bandwidth {
563
    /// The volume that the relay is willing to sustain over long periods.
564
    #[deftly(constructor)]
565
    pub average: u64,
566

            
567
    /// The volume that the relay is willing to sustain in very short intervals.
568
    #[deftly(constructor)]
569
    pub burst: u64,
570

            
571
    /// The estimate of the capacity this relay can handle.
572
    #[deftly(constructor)]
573
    pub observed: u64,
574

            
575
    #[doc(hidden)]
576
    #[deftly(netdoc(skip))]
577
    pub __non_exhaustive: (),
578
}
579

            
580
/// Ntor onion key cross-certificate.
581
///
582
/// This struct contains an [`Ed25519NtorCrossCert`] alongside the `bit`
583
/// field required for converting the ntor X25519 key to an Ed25519 key.
584
///
585
/// # See Also
586
///
587
/// * [`Ed25519NtorCrossCert`]
588
/// * <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:ntor-onion-key-crosscert>
589
#[derive(Debug, Clone, Deftly, PartialEq)]
590
#[derive_deftly(Constructor, ItemValueParseable, ItemValueEncodable)]
591
#[deftly(netdoc(no_extra_args))]
592
#[allow(clippy::exhaustive_structs)]
593
pub struct NtorOnionKeyCrossCert {
594
    /// True if X coordinate of the ntor onion key is negative, false if
595
    /// positive.
596
    // TODO spec: This name is very unfortunate, how about we change it
597
    // to `is_negative`.  Also, using a boolean for storing a sign bit feels
598
    // wrong to me due to the zero edge case, which would not be negative,
599
    // but also not positive either.
600
    #[deftly(constructor)]
601
    pub bit: NumericBoolean,
602

            
603
    /// The actual embedded ntor onion key certificate.
604
    #[deftly(netdoc(object))]
605
    #[deftly(constructor)]
606
    pub cert: EmbeddedCert<Ed25519NtorCrossCert, KeyUnknownCert>,
607

            
608
    #[doc(hidden)]
609
    #[deftly(netdoc(skip))]
610
    pub __non_exhaustive: (),
611
}
612

            
613
decl_keyword! {
614
    /// RouterKwd is an instance of Keyword, used to denote the different
615
    /// Items that are recognized as appearing in a router descriptor.
616
    RouterKwd {
617
        annotation "@source" => ANN_SOURCE,
618
        annotation "@downloaded-at" => ANN_DOWNLOADED_AT,
619
        annotation "@purpose" => ANN_PURPOSE,
620
        "accept" | "reject" => POLICY,
621
        "bandwidth" => BANDWIDTH,
622
        "bridge-distribution-request" => BRIDGE_DISTRIBUTION_REQUEST,
623
        "caches-extra-info" => CACHES_EXTRA_INFO,
624
        "contact" => CONTACT,
625
        "extra-info-digest" => EXTRA_INFO_DIGEST,
626
        "family" => FAMILY,
627
        "family-cert" => FAMILY_CERT,
628
        "fingerprint" => FINGERPRINT,
629
        "hibernating" => HIBERNATING,
630
        "identity-ed25519" => IDENTITY_ED25519,
631
        "ipv6-policy" => IPV6_POLICY,
632
        "master-key-ed25519" => MASTER_KEY_ED25519,
633
        "ntor-onion-key" => NTOR_ONION_KEY,
634
        "ntor-onion-key-crosscert" => NTOR_ONION_KEY_CROSSCERT,
635
        "or-address" => OR_ADDRESS,
636
        "platform" => PLATFORM,
637
        "proto" => PROTO,
638
        "published" => PUBLISHED,
639
        "router" => ROUTER,
640
        "router-sig-ed25519" => ROUTER_SIG_ED25519,
641
        "router-signature" => ROUTER_SIGNATURE,
642
        "signing-key" => SIGNING_KEY,
643
        "tunnelled_dir_server" => TUNNELLED_DIR_SERVER,
644
        "uptime" => UPTIME,
645
        // "protocols" once existed, but is obsolete
646
        // "eventdns" once existed, but is obsolete
647
        // "allow-single-hop-exits" is also obsolete.
648
    }
649
}
650

            
651
/// Rules for parsing a set of router descriptor annotations.
652
1
static ROUTER_ANNOTATIONS: LazyLock<SectionRules<RouterKwd>> = LazyLock::new(|| {
653
    use RouterKwd::*;
654

            
655
1
    let mut rules = SectionRules::builder();
656
1
    rules.add(ANN_SOURCE.rule());
657
1
    rules.add(ANN_DOWNLOADED_AT.rule().args(1..));
658
1
    rules.add(ANN_PURPOSE.rule().args(1..));
659
1
    rules.add(ANN_UNRECOGNIZED.rule().may_repeat().obj_optional());
660
    // Unrecognized annotations are fine; anything else is an error in this
661
    // context.
662
1
    rules.reject_unrecognized();
663
1
    rules.build()
664
1
});
665
/// Rules for tokens that are allowed in the first part of a
666
/// router descriptor.
667
54
static ROUTER_HEADER_RULES: LazyLock<SectionRules<RouterKwd>> = LazyLock::new(|| {
668
    use RouterKwd::*;
669

            
670
54
    let mut rules = SectionRules::builder();
671
54
    rules.add(ROUTER.rule().required().args(5..));
672
54
    rules.add(IDENTITY_ED25519.rule().required().no_args().obj_required());
673
    // No other intervening tokens are permitted in the header.
674
54
    rules.reject_unrecognized();
675
54
    rules.build()
676
54
});
677
/// Rules for  tokens that are allowed in the first part of a
678
/// router descriptor.
679
54
static ROUTER_BODY_RULES: LazyLock<SectionRules<RouterKwd>> = LazyLock::new(|| {
680
    use RouterKwd::*;
681

            
682
54
    let mut rules = SectionRules::builder();
683
54
    rules.add(MASTER_KEY_ED25519.rule().required().args(1..));
684
54
    rules.add(PLATFORM.rule());
685
54
    rules.add(PUBLISHED.rule().required());
686
54
    rules.add(FINGERPRINT.rule());
687
54
    rules.add(UPTIME.rule().args(1..));
688
54
    rules.add(NTOR_ONION_KEY.rule().required().args(1..));
689
54
    rules.add(
690
54
        NTOR_ONION_KEY_CROSSCERT
691
54
            .rule()
692
54
            .required()
693
54
            .args(1..=1)
694
54
            .obj_required(),
695
    );
696
54
    rules.add(SIGNING_KEY.rule().no_args().required().obj_required());
697
54
    rules.add(POLICY.rule().may_repeat().args(1..));
698
54
    rules.add(IPV6_POLICY.rule().args(2..));
699
54
    rules.add(FAMILY.rule().args(1..));
700
54
    rules.add(FAMILY_CERT.rule().obj_required().may_repeat());
701
54
    rules.add(CACHES_EXTRA_INFO.rule().no_args());
702
54
    rules.add(OR_ADDRESS.rule().may_repeat().args(1..));
703
54
    rules.add(TUNNELLED_DIR_SERVER.rule());
704
54
    rules.add(PROTO.rule().required().args(1..));
705
54
    rules.add(UNRECOGNIZED.rule().may_repeat().obj_optional());
706
    // TODO: these aren't parsed yet.  Only authorities use them.
707
54
    {
708
54
        rules.add(BANDWIDTH.rule().required().args(3..));
709
54
        rules.add(BRIDGE_DISTRIBUTION_REQUEST.rule().args(1..));
710
54
        rules.add(HIBERNATING.rule().args(1..));
711
54
        rules.add(CONTACT.rule());
712
54
    }
713
    // TODO: this is ignored for now.
714
54
    {
715
54
        rules.add(EXTRA_INFO_DIGEST.rule().args(1..));
716
54
    }
717
54
    rules.build()
718
54
});
719

            
720
/// Rules for items that appear at the end of a router descriptor.
721
54
static ROUTER_SIG_RULES: LazyLock<SectionRules<RouterKwd>> = LazyLock::new(|| {
722
    use RouterKwd::*;
723

            
724
54
    let mut rules = SectionRules::builder();
725
54
    rules.add(ROUTER_SIG_ED25519.rule().required().args(1..));
726
54
    rules.add(ROUTER_SIGNATURE.rule().required().no_args().obj_required());
727
    // No intervening tokens are allowed in the footer.
728
54
    rules.reject_unrecognized();
729
54
    rules.build()
730
54
});
731

            
732
impl RouterAnnotation {
733
    /// Extract a single RouterAnnotation (possibly empty) from a reader.
734
4
    fn take_from_reader(reader: &mut NetDocReader<'_, RouterKwd>) -> Result<RouterAnnotation> {
735
        use RouterKwd::*;
736
8
        let mut items = reader.pause_at(|item| item.is_ok_with_non_annotation());
737

            
738
4
        let body = ROUTER_ANNOTATIONS.parse(&mut items)?;
739

            
740
3
        let source = body.maybe(ANN_SOURCE).args_as_str().map(String::from);
741
3
        let purpose = body.maybe(ANN_PURPOSE).args_as_str().map(String::from);
742
3
        let downloaded = body
743
3
            .maybe(ANN_DOWNLOADED_AT)
744
3
            .parse_args_as_str::<Iso8601TimeSp>()?
745
3
            .map(|t| t.into());
746
3
        Ok(RouterAnnotation {
747
3
            source,
748
3
            downloaded,
749
3
            purpose,
750
3
        })
751
4
    }
752
}
753

            
754
/// A parsed router descriptor whose signatures and/or validity times
755
/// may or may not be invalid.
756
pub type UncheckedRouterDesc = signed::SignatureGated<timed::TimeRangeBound<RouterDesc>>;
757

            
758
/// How long after its published time is a router descriptor officially
759
/// supposed to be usable?
760
const ROUTER_EXPIRY_SECONDS: u64 = 5 * 86400;
761

            
762
/// How long before its published time is a router descriptor usable?
763
// TODO(nickm): This valid doesn't match C tor, which only enforces this rule
764
// ("routers should not some from the future") at directory authorities, and
765
// there only enforces a 12-hour limit (`ROUTER_ALLOW_SKEW`).  Eventually we
766
// should probably harmonize these cutoffs.
767
const ROUTER_PRE_VALIDITY_SECONDS: u64 = 86400;
768

            
769
impl RouterDesc {
770
    /// Encode this router descriptor and append its Ed25519 and RSA signatures in that
771
    /// specific order.
772
    ///
773
    /// `k_relayid_rsa` must match [`Self::signing_key`] and `k_relaysign_ed`
774
    /// must match the relay signing key certified by [`Self::identity_ed25519`].
775
    /// Returns an error if either key's public part does not match.
776
    ///
777
    /// Returns the signed and encoded document as a string.
778
13
    pub fn encode_sign(
779
13
        &self,
780
13
        k_relayid_rsa: &rsa::KeyPair,
781
13
        k_relaysign_ed: &ed25519::Keypair,
782
13
    ) -> Result<String> {
783
        use RouterKwd::{ROUTER_SIG_ED25519, ROUTER_SIGNATURE};
784

            
785
        // Extra precaution as non matching keys will successfully create a non usable
786
        // descriptor with keys not matching the signatures.
787
13
        if k_relayid_rsa.to_public_key() != self.signing_key {
788
2
            return Err(internal!("RSA identity key does not match the one stored").into());
789
11
        }
790
11
        if Ed25519Identity::from(k_relaysign_ed.public_key())
791
11
            != self.identity_ed25519.get()?.sign_ed25519
792
        {
793
1
            return Err(internal!("Ed25519 signing key does not match the one stored").into());
794
10
        }
795

            
796
10
        let mut encoder = NetdocEncoder::new();
797
10
        self.encode_unsigned(&mut encoder)?;
798

            
799
        // Note that we can't use RouterDescSignatures because of the overlapping
800
        // signatures that is the RSA signature covers the Ed25519 signatures.
801

            
802
10
        let ed_signature = RouterSigEd25519::new_sign_netdoc(
803
10
            k_relaysign_ed,
804
10
            &encoder,
805
10
            ROUTER_SIG_ED25519.to_str(),
806
        )?;
807
10
        ed_signature.write_item_value_onto(encoder.item(ROUTER_SIG_ED25519))?;
808

            
809
        // The RSA signature also covers the preceding Ed25519 signature item.
810
10
        let rsa_signature = RouterSignature(
811
10
            RsaSha1Signature::new_sign_netdoc(k_relayid_rsa, &encoder, ROUTER_SIGNATURE.to_str())?
812
                .signature,
813
        );
814
10
        rsa_signature.write_item_value_onto(encoder.item(ROUTER_SIGNATURE))?;
815

            
816
10
        Ok(encoder.finish()?)
817
13
    }
818

            
819
    /// Return a reference to this relay's RSA identity.
820
1273
    pub fn rsa_identity(&self) -> RsaIdentity {
821
1273
        self.signing_key.to_rsa_identity()
822
1273
    }
823

            
824
    /// Return a reference to this relay's Ed25519 identity.
825
1
    pub fn ed_identity(&self) -> &Ed25519Identity {
826
1
        &self
827
1
            .identity_ed25519
828
1
            .get()
829
1
            .expect("ed25519 identity cert should be verified")
830
1
            .id_ed25519
831
1
    }
832

            
833
    /// Return a reference to the list of subprotocol versions supported by this
834
    /// relay.
835
1
    pub fn protocols(&self) -> &tor_protover::Protocols {
836
1
        &self.proto
837
1
    }
838

            
839
    /// Return a reference to this relay's Ntor onion key.
840
1
    pub fn ntor_onion_key(&self) -> &ll::pk::curve25519::PublicKey {
841
1
        &self.ntor_onion_key.0
842
1
    }
843

            
844
    /// Return the publication
845
319
    pub fn published(&self) -> time::SystemTime {
846
319
        self.published.0
847
319
    }
848

            
849
    /// Return an iterator of every `SocketAddr` at which this descriptor says
850
    /// its relay can be reached.
851
1
    pub fn or_ports(&self) -> impl Iterator<Item = net::SocketAddr> + '_ {
852
1
        iter::once(net::SocketAddr::new(
853
1
            self.router.address.into(),
854
1
            self.router.orport,
855
        ))
856
1
        .chain(self.or_address.iter().copied())
857
1
    }
858

            
859
    /// Return the declared family of this descriptor.
860
    pub fn family(&self) -> Intern<RelayFamily> {
861
        Intern::clone(&self.family)
862
    }
863

            
864
    /// Return the authenticated family IDs of this descriptor.
865
1
    pub fn family_ids(&self) -> RelayFamilyIds {
866
1
        RelayFamilyIds::from_iter(
867
1
            self.family_cert
868
1
                .iter()
869
2
                .map(|cert| cert.get().expect("unverified family cert?"))
870
2
                .map(|cert| cert.family_ed25519.into()),
871
        )
872
1
    }
873

            
874
    /// Helper: tokenize `s`, and divide it into three validated sections.
875
2349
    fn parse_sections<'a>(
876
2349
        reader: &mut NetDocReader<'a, RouterKwd>,
877
2349
    ) -> Result<(
878
2349
        Section<'a, RouterKwd>,
879
2349
        Section<'a, RouterKwd>,
880
2349
        Section<'a, RouterKwd>,
881
2349
    )> {
882
        use RouterKwd::*;
883

            
884
        // Parse everything up through the header.
885
2349
        let header = ROUTER_HEADER_RULES.parse(
886
6832
            reader.pause_at(|item| item.is_ok_with_kwd_not_in(&[ROUTER, IDENTITY_ED25519])),
887
108
        )?;
888

            
889
        // Parse everything up to but not including the signature.
890
2241
        let body =
891
38119
            ROUTER_BODY_RULES.parse(reader.pause_at(|item| {
892
38119
                item.is_ok_with_kwd_in(&[ROUTER_SIGNATURE, ROUTER_SIG_ED25519])
893
38119
            }))?;
894

            
895
        // Parse the signature.
896
4489
        let sig = ROUTER_SIG_RULES.parse(reader.pause_at(|item| {
897
4489
            item.is_ok_with_annotation() || item.is_ok_with_kwd(ROUTER) || item.is_empty_line()
898
4489
        }))?;
899

            
900
2241
        Ok((header, body, sig))
901
2349
    }
902

            
903
    /// Try to parse `s` as a router descriptor.
904
    ///
905
    /// Does not actually check liveness or signatures; you need to do that
906
    /// yourself before you can do the output.
907
    ///
908
    /// The following fields are not parsed with the legacy parser and their
909
    /// default value is used instead.
910
    /// * [`RouterDescIntroItem::socksport`] in [`RouterDesc::router`]
911
    /// * [`RouterDesc::bandwidth`]
912
    /// * [`RouterDesc::or_address`]
913
    ///     * Extracts only the first IPv6 address.
914
    /// * [`RouterDesc::hibernating`]
915
    /// * [`RouterDesc::overload_general`]
916
    /// * [`RouterDesc::contact`]
917
    /// * [`RouterDesc::extra_info_digest`]
918
    /// * [`RouterDesc::hidden_service_dir`]
919
2346
    pub fn parse(s: &str) -> Result<UncheckedRouterDesc> {
920
2346
        let mut reader = crate::parse::tokenize::NetDocReader::new(s)?;
921
2346
        let result = Self::parse_internal(&mut reader).map_err(|e| e.within(s))?;
922
        // We permit empty lines at the end of router descriptors, since there's
923
        // a known issue in Tor relays that causes them to return them this way.
924
2229
        reader
925
2229
            .should_be_exhausted_but_for_empty_lines()
926
2229
            .map_err(|e| e.within(s))?;
927
2229
        Ok(result)
928
2346
    }
929

            
930
    /// Helper: parse a router descriptor from `s`.
931
    ///
932
    /// This function does the same as parse(), but returns errors based on
933
    /// byte-wise positions.  The parse() function converts such errors
934
    /// into line-and-byte positions.
935
2349
    fn parse_internal(r: &mut NetDocReader<'_, RouterKwd>) -> Result<UncheckedRouterDesc> {
936
        // TODO: This function is too long!  The little "paragraphs" here
937
        // that parse one item at a time should be made into sub-functions.
938
        use RouterKwd::*;
939

            
940
2349
        let s = r.str();
941
2349
        let (header, body, sig) = RouterDesc::parse_sections(r)?;
942

            
943
        // Unwrap should be safe because inline `required` call should return
944
        // `Error::MissingToken` if `ROUTER` is not `Ok`
945
        #[allow(clippy::unwrap_used)]
946
2241
        let start_offset = header.required(ROUTER)?.offset_in(s).unwrap();
947

            
948
        // ed25519 identity and signing key.
949
        //
950
        // Small digression: This is terrible.  We return a tuple containing
951
        // a KeyUnknownCert and an UncheckedCert.  This is because of a parse2
952
        // and legacy incongruence.  For parse2, we need the KeyUnknownCert
953
        // to properly include it into EmbeddedCert, whereas the legacy parser
954
        // will need an UncheckedCert because the verification chain is
955
        // performed at the end.  Because tor-cert's method all consume self,
956
        // we can not go backwards, meaning we have to store two separate
957
        // copies.  It is also not possible to do the conversion to
958
        // UncheckedCert later, because then we lose the error context returned
959
        // in EK::BadObjectVal if the signed-by extension is missing.
960
        //
961
2237
        let (ku_identity_cert, identity_cert, ed25519_signing_key) = {
962
2241
            let cert_tok = header.required(IDENTITY_ED25519)?;
963
            // Unwrap should be safe because above `required` call should
964
            // return `Error::MissingToken` if `IDENTITY_ED25519` is not `Ok`
965
            #[allow(clippy::unwrap_used)]
966
2241
            if cert_tok.offset_in(s).unwrap() < start_offset {
967
1
                return Err(EK::MisplacedToken
968
1
                    .with_msg("identity-ed25519")
969
1
                    .at_pos(cert_tok.pos()));
970
2240
            }
971
2240
            let ku_cert = cert_tok
972
2240
                .parse_obj::<UnvalidatedEdCert>("ED25519 CERT")?
973
2240
                .check_cert_type(tor_cert::CertType::IDENTITY_V_SIGNING)?
974
2240
                .into_unchecked();
975
2240
            let cert = ku_cert.clone().should_have_signing_key().map_err(|err| {
976
1
                EK::BadObjectVal
977
1
                    .err()
978
1
                    .with_source(err)
979
1
                    .at_pos(cert_tok.pos())
980
1
            })?;
981
2239
            let sk = *cert.peek_subject_key().as_ed25519().ok_or_else(|| {
982
1
                EK::BadObjectVal
983
1
                    .at_pos(cert_tok.pos())
984
1
                    .with_msg("wrong type for signing key in cert")
985
1
            })?;
986
2238
            let sk: ll::pk::ed25519::PublicKey = sk.try_into().map_err(|_| {
987
1
                EK::BadObjectVal
988
1
                    .at_pos(cert_tok.pos())
989
1
                    .with_msg("invalid ed25519 signing key")
990
1
            })?;
991
2237
            (ku_cert, cert, sk)
992
        };
993

            
994
        // master-key-ed25519: required, and should match certificate.
995
        #[allow(unexpected_cfgs)]
996
2236
        let ed25519_identity_key = {
997
2237
            let master_key_tok = body.required(MASTER_KEY_ED25519)?;
998
2237
            let ed_id: Ed25519Public = master_key_tok.parse_arg(0)?;
999
2237
            let ed_id: ll::pk::ed25519::Ed25519Identity = ed_id.into();
2237
            if ed_id != *identity_cert.peek_signing_key() {
                #[cfg(not(fuzzing))] // No feature here; never omit in production.
1
                return Err(EK::BadObjectVal
1
                    .at_pos(master_key_tok.pos())
1
                    .with_msg("master-key-ed25519 does not match key in identity-ed25519"));
2236
            }
2236
            ed_id
        };
        // Legacy RSA identity
2236
        let rsa_identity_key: ll::pk::rsa::PublicKey = body
2236
            .required(SIGNING_KEY)?
2236
            .parse_obj::<RsaPublicParse1Helper>("RSA PUBLIC KEY")?
2236
            .check_len_eq(1024)?
2236
            .check_exponent(65537)?
2236
            .into();
2236
        let rsa_identity = rsa_identity_key.to_rsa_identity();
2236
        let ed_sig = sig.required(ROUTER_SIG_ED25519)?;
2236
        let rsa_sig = sig.required(ROUTER_SIGNATURE)?;
        // Unwrap should be safe because above `required` calls should return
        // an `Error::MissingToken` if `ROUTER_...` is not `Ok`
        #[allow(clippy::unwrap_used)]
2236
        let ed_sig_pos = ed_sig.offset_in(s).unwrap();
        #[allow(clippy::unwrap_used)]
2236
        let rsa_sig_pos = rsa_sig.offset_in(s).unwrap();
2236
        if ed_sig_pos > rsa_sig_pos {
1
            return Err(EK::UnexpectedToken
1
                .with_msg(ROUTER_SIG_ED25519.to_str())
1
                .at_pos(ed_sig.pos()));
2235
        }
        // Extract ed25519 signature.
2235
        let ed_signature: ll::pk::ed25519::ValidatableEd25519Signature = {
2235
            let mut d = ll::d::Sha256::new();
2235
            d.update(&b"Tor router descriptor signature v1"[..]);
2235
            let signed_end = ed_sig_pos + b"router-sig-ed25519 ".len();
2235
            d.update(
2235
                s.get(start_offset..signed_end)
2235
                    .ok_or_else(|| internal!("chopped utf8"))?,
            );
2235
            let d = d.finalize();
2235
            let sig: [u8; 64] = ed_sig
2235
                .parse_arg::<B64>(0)?
2235
                .into_array()
2235
                .map_err(|_| EK::BadSignature.at_pos(ed_sig.pos()))?;
2235
            let sig = ll::pk::ed25519::Signature::from(sig);
2235
            ll::pk::ed25519::ValidatableEd25519Signature::new(ed25519_signing_key, sig, &d)
        };
        // Extract legacy RSA signature.
2235
        let rsa_signature: ll::pk::rsa::ValidatableRsaSignature = {
2235
            let mut d = ll::d::Sha1::new();
2235
            let signed_end = rsa_sig_pos + b"router-signature\n".len();
2235
            d.update(
2235
                s.get(start_offset..signed_end)
2235
                    .ok_or_else(|| internal!("chopped utf8"))?,
            );
2235
            let d = d.finalize();
2235
            let sig = rsa_sig.obj("SIGNATURE")?;
            // TODO: we need to accept prefixes here. COMPAT BLOCKER.
2235
            ll::pk::rsa::ValidatableRsaSignature::new(&rsa_identity_key, &sig, &d)
        };
        // router nickname ipv4addr orport socksport dirport
2235
        let (nickname, ipv4addr, orport, dirport) = {
2235
            let rtrline = header.required(ROUTER)?;
            (
2235
                rtrline.required_arg(0)?.parse::<Nickname>().map_err(|e| {
                    EK::BadArgument
                        .with_msg(e.to_string())
                        .at_pos(rtrline.pos())
                })?,
2235
                rtrline.parse_arg::<net::Ipv4Addr>(1)?,
2235
                rtrline.parse_arg(2)?,
                // Skipping socksport.
2235
                rtrline.parse_arg(4)?,
            )
        };
        // uptime
2235
        let uptime = body.maybe(UPTIME).parse_arg(0)?;
        // published time.
2235
        let published = body
2235
            .required(PUBLISHED)?
2235
            .args_as_str()
2235
            .parse::<Iso8601TimeSp>()?;
        // ntor key
2235
        let ntor_onion_key: Curve25519Public = body.required(NTOR_ONION_KEY)?.parse_arg(0)?;
        // ntor crosscert
2233
        let (cc_sig, cc_expiry, cc_cert) = {
2235
            let cc = body.required(NTOR_ONION_KEY_CROSSCERT)?;
2235
            let sign: u8 = cc.parse_arg(0)?;
2235
            if sign != 0 && sign != 1 {
2
                return Err(EK::BadArgument.at_pos(cc.arg_pos(0)).with_msg("not 0 or 1"));
2233
            }
2233
            let ntor_as_ed: ll::pk::ed25519::PublicKey =
2233
                ll::pk::keymanip::convert_curve25519_to_ed25519_public(&ntor_onion_key.0, sign)
2233
                    .ok_or_else(|| {
                        EK::BadArgument
                            .at_pos(cc.pos())
                            .with_msg("Uncheckable crosscert")
                    })?;
2233
            let cert = cc
2233
                .parse_obj::<UnvalidatedEdCert>("ED25519 CERT")?
2233
                .into_unchecked();
2233
            let (_, sig, expiry) = Ed25519NtorCrossCert::verify_inner(
2233
                ntor_as_ed.into(),
2233
                ed25519_identity_key,
2233
                cert.clone(),
            )
2233
            .map_err(|_| EK::BadSignature.err())?;
2233
            let cert = NtorOnionKeyCrossCertConstructor {
2233
                bit: NumericBoolean(sign != 0),
2233
                // Okay to call because we added the signature to the batch.
2233
                cert: EmbeddedCert::new(Ed25519NtorCrossCert::dangerous_new_unverified(), cert),
2233
            }
2233
            .construct();
2233
            (sig, expiry, cert)
        };
        // List of subprotocol versions
2233
        let proto = {
2233
            let proto_tok = body.required(PROTO)?;
2233
            proto_tok
2233
                .args_as_str()
2233
                .parse::<tor_protover::Protocols>()
2233
                .map_err(|e| EK::BadArgument.at_pos(proto_tok.pos()).with_source(e))?
        };
        // tunneled-dir-server
2233
        let is_dircache = ((dirport != 0) || body.get(TUNNELLED_DIR_SERVER).is_some())
2233
            .then_some(ItemPresent::default());
        // caches-extra-info
2233
        let is_extrainfo_cache = body.get(CACHES_EXTRA_INFO).map(|_| ItemPresent::default());
        // fingerprint: check for consistency with RSA identity.
2233
        if let Some(fp_tok) = body.get(FINGERPRINT) {
2233
            let fp: RsaIdentity = fp_tok.args_as_str().parse::<SpFingerprint>()?.into();
2233
            if fp != rsa_identity {
2
                return Err(EK::BadArgument
2
                    .at_pos(fp_tok.pos())
2
                    .with_msg("fingerprint does not match RSA identity"));
2231
            }
        }
        // Family
2231
        let family = {
2231
            let mut family = body
2231
                .maybe(FAMILY)
2231
                .parse_args_as_str::<RelayFamily>()?
2231
                .unwrap_or_else(RelayFamily::new);
2231
            if !family.is_empty() {
2
                // If this family is nonempty, we add our own RSA id to it, on
2
                // the theory that doing so will improve the odds of having a
2
                // canonical family shared by all of the members of this family.
2
                // If the family is empty, there's no point in adding our own ID
2
                // to it, and doing so would only waste memory.
2
                family.push(rsa_identity);
2229
            }
2231
            family.intern()
        };
        // Family ids (for "happy families")
        //
        // Unfortunately we have to store this as a tuple of KeyUnknownCert and
        // UncheckedCert due to a parse2/legacy incongruence.  parse2 requires
        // KeyUnknownCert for EmbeddedCert whereas the legacy parser needs
        // descendants of it obtained by passing it irreversibly through the
        // tor_cert verification chain.
2231
        let family_certs = body
2231
            .slice(FAMILY_CERT)
2231
            .iter()
2231
            .map(|ent| {
2
                let ku = ent
2
                    .parse_obj::<UnvalidatedEdCert>("FAMILY CERT")?
2
                    .check_cert_type(CertType::FAMILY_V_IDENTITY)?
2
                    .check_subject_key_is(identity_cert.peek_signing_key())?
2
                    .into_unchecked();
2
                let unchecked = ku.clone().should_have_signing_key().map_err(|e| {
                    EK::BadObjectVal
                        .with_msg("missing public key")
                        .at_pos(ent.pos())
                        .with_source(e)
                })?;
2
                Ok((ku, unchecked))
2
            })
2231
            .collect::<Result<Vec<_>>>()?;
        // or-address
        // Extract at most one ipv6 address from the list.  It's not great,
        // but it's what the legacy parser does.
2231
        let mut ipv6addr = Vec::with_capacity(1);
2231
        for tok in body.slice(OR_ADDRESS) {
3
            if let Ok(net::SocketAddr::V6(a)) = tok.parse_arg::<net::SocketAddr>(0) {
3
                ipv6addr.push(a.into());
3
                break;
            }
            // We skip over unparsable addresses. Is that right?
        }
        // platform
2231
        let platform = body.maybe(PLATFORM).parse_args_as_str::<RelayPlatform>()?;
        // ipv4_policy
2231
        let ipv4_policy = {
2231
            let mut pol = AddrPolicy::new();
2232
            for ruletok in body.slice(POLICY).iter() {
2232
                let accept = match ruletok.kwd_str() {
2232
                    "accept" => RuleKind::Accept,
2231
                    "reject" => RuleKind::Reject,
                    _ => {
                        return Err(Error::from(internal!(
                            "tried to parse a strange line as a policy"
                        ))
                        .at_pos(ruletok.pos()));
                    }
                };
2232
                let pat: AddrPortPattern = ruletok
2232
                    .args_as_str()
2232
                    .parse()
2232
                    .map_err(|e| EK::BadPolicy.at_pos(ruletok.pos()).with_source(e))?;
2232
                pol.push(accept, pat);
            }
2231
            pol
        };
        // ipv6 policy
2231
        let ipv6_policy = match body.get(IPV6_POLICY) {
1
            Some(p) => p
1
                .args_as_str()
1
                .parse()
1
                .map_err(|e| EK::BadPolicy.at_pos(p.pos()).with_source(e))?,
            // Unwrap is safe here because str is not empty
            #[allow(clippy::unwrap_used)]
2230
            None => "reject 1-65535".parse::<PortPolicy>().unwrap(),
        };
        // Now we're going to collect signatures and expiration times.
2230
        let (identity_cert, identity_sig) = identity_cert.dangerously_split().map_err(|err| {
            EK::BadObjectVal
                .with_msg("missing public key")
                .with_source(err)
        })?;
2230
        let mut signatures: Vec<Box<dyn ll::pk::ValidatableSignature>> = vec![
2230
            Box::new(rsa_signature),
2230
            Box::new(ed_signature),
2230
            Box::new(identity_sig),
2230
            Box::new(cc_sig),
        ];
2230
        let identity_cert = identity_cert.dangerously_assume_timely();
2230
        let mut expirations = vec![
2230
            published
2230
                .0
2230
                .saturating_add(time::Duration::new(ROUTER_EXPIRY_SECONDS, 0)),
2230
            identity_cert.expiry(),
2230
            cc_expiry,
        ];
        // As outlined above, we have to do this ... :/
        //
        // Composing the verified part of the EmbeddedCert by just extracting
        // the key alone is OK because it gets checked at the end anyways
        // due to the push to signatures and expirations.
2230
        let mut embedded_family_certs = Vec::with_capacity(family_certs.len());
2230
        for (ku_cert, cert) in family_certs {
2
            let family_ed25519 = *cert.peek_signing_key();
2
            let (inner, sig) = cert.dangerously_split().map_err(into_internal!(
                "Missing a public key that was previously there."
            ))?;
2
            let embedded_cert = EmbeddedCert::new(Ed25519FamilyCert { family_ed25519 }, ku_cert);
2
            signatures.push(Box::new(sig));
2
            expirations.push(inner.dangerously_assume_timely().expiry());
2
            embedded_family_certs.push(embedded_cert);
        }
        // Unwrap is safe here because `expirations` array is not empty
        #[allow(clippy::unwrap_used)]
2230
        let expiry = *expirations.iter().min().unwrap();
2230
        let start_time = published
2230
            .0
2230
            .saturating_sub(time::Duration::new(ROUTER_PRE_VALIDITY_SECONDS, 0));
2230
        let desc = RouterDesc {
2230
            router: RouterDescIntroItem {
2230
                dirport,
2230
                ..RouterDescIntroItemConstructor {
2230
                    nickname,
2230
                    address: ipv4addr,
2230
                    orport,
2230
                }
2230
                .construct()
2230
            },
2230
            identity_ed25519: EmbeddedCert::new(
2230
                Ed25519IdentityCert {
2230
                    id_ed25519: ed25519_identity_key,
2230
                    sign_ed25519: ed25519_signing_key.into(),
2230
                },
2230
                ku_identity_cert,
2230
            ),
2230
            master_key_ed25519: ed25519_identity_key.into(),
2230
            bandwidth: Default::default(),
2230
            platform,
2230
            published,
2230
            fingerprint: Some(rsa_identity.into()),
2230
            hibernating: Default::default(),
2230
            uptime,
2230
            ntor_onion_key,
2230
            ntor_onion_key_crosscert: cc_cert,
2230
            signing_key: rsa_identity_key,
2230
            ipv4_policy,
2230
            ipv6_policy: ipv6_policy.intern(),
2230
            overload_general: Default::default(),
2230
            contact: Default::default(),
2230
            family,
2230
            family_cert: embedded_family_certs.into(),
2230
            caches_extra_info: is_extrainfo_cache,
2230
            extra_info_digest: Default::default(),
2230
            hidden_service_dir: Default::default(),
2230
            or_address: ipv6addr,
2230
            tunnelled_dir_server: is_dircache,
2230
            proto,
2230
            __non_exhaustive: (),
2230
        };
2230
        let time_gated = timed::TimeRangeBound::new(desc, start_time..expiry);
2230
        let sig_gated = signed::SignatureGated::new(time_gated, signatures);
2230
        Ok(sig_gated)
2349
    }
}
/// An iterator that parses one or more (possibly annotated
/// router descriptors from a string.
//
// TODO: This is largely copy-pasted from MicrodescReader. Can/should they
// be merged?
pub struct RouterReader<'a> {
    /// True iff we accept annotations
    annotated: bool,
    /// Reader that we're extracting items from.
    reader: NetDocReader<'a, RouterKwd>,
}
/// Skip this reader forward until the next thing it reads looks like the
/// start of a router descriptor.
///
/// Used to recover from errors.
3
fn advance_to_next_routerdesc(reader: &mut NetDocReader<'_, RouterKwd>, annotated: bool) {
    use RouterKwd::*;
    loop {
3
        let item = reader.peek();
2
        match item {
2
            Some(Ok(t)) => {
2
                let kwd = t.kwd();
2
                if (annotated && kwd.is_annotation()) || kwd == ROUTER {
2
                    return;
                }
            }
            Some(Err(_)) => {
                // Skip over broken tokens.
            }
            None => {
1
                return;
            }
        }
        let _ = reader.next();
    }
3
}
impl<'a> RouterReader<'a> {
    /// Construct a RouterReader to take router descriptors from a string.
1
    pub fn new(s: &'a str, allow: &AllowAnnotations) -> Result<Self> {
1
        let reader = NetDocReader::new(s)?;
1
        let annotated = allow == &AllowAnnotations::AnnotationsAllowed;
1
        Ok(RouterReader { annotated, reader })
1
    }
    /// Extract an annotation from this reader.
4
    fn take_annotation(&mut self) -> Result<RouterAnnotation> {
4
        if self.annotated {
4
            RouterAnnotation::take_from_reader(&mut self.reader)
        } else {
            Ok(RouterAnnotation::default())
        }
4
    }
    /// Extract an annotated router descriptor from this reader
    ///
    /// (internal helper; does not clean up on failures.)
4
    fn take_annotated_routerdesc_raw(&mut self) -> Result<AnnotatedRouterDesc> {
4
        let ann = self.take_annotation()?;
3
        let router = RouterDesc::parse_internal(&mut self.reader)?;
1
        Ok(AnnotatedRouterDesc { ann, router })
4
    }
    /// Extract an annotated router descriptor from this reader
    ///
    /// Ensure that at least one token is consumed
4
    fn take_annotated_routerdesc(&mut self) -> Result<AnnotatedRouterDesc> {
4
        let pos_orig = self.reader.pos();
4
        let result = self.take_annotated_routerdesc_raw();
4
        if result.is_err() {
3
            if self.reader.pos() == pos_orig {
                // No tokens were consumed from the reader.  We need
                // to drop at least one token to ensure we aren't in
                // an infinite loop.
                //
                // (This might not be able to happen, but it's easier to
                // explicitly catch this case than it is to prove that
                // it's impossible.)
                let _ = self.reader.next();
3
            }
3
            advance_to_next_routerdesc(&mut self.reader, self.annotated);
1
        }
4
        result
4
    }
}
impl<'a> Iterator for RouterReader<'a> {
    type Item = Result<AnnotatedRouterDesc>;
5
    fn next(&mut self) -> Option<Self::Item> {
        // Is there a next token? If not, we're done.
5
        self.reader.peek()?;
        Some(
4
            self.take_annotated_routerdesc()
4
                .map_err(|e| e.within(self.reader.str())),
        )
5
    }
}
#[cfg(test)]
mod test {
    // @@ begin test lint list maintained by maint/add_warning @@
    #![allow(clippy::bool_assert_comparison)]
    #![allow(clippy::clone_on_copy)]
    #![allow(clippy::dbg_macro)]
    #![allow(clippy::mixed_attributes_style)]
    #![allow(clippy::print_stderr)]
    #![allow(clippy::print_stdout)]
    #![allow(clippy::single_char_pattern)]
    #![allow(clippy::unwrap_used)]
    #![allow(clippy::unchecked_time_subtraction)]
    #![allow(clippy::useless_vec)]
    #![allow(clippy::needless_pass_by_value)]
    #![allow(clippy::string_slice)] // See arti#2571
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
    use std::{net::Ipv4Addr, str::FromStr, time::Duration};
    use crate::{
        encode::{NetdocEncodable, NetdocEncoder},
        parse2::{self, NetdocParseableUnverified, ParseInput},
    };
    use tor_basic_utils::test_rng::testing_rng;
    use tor_checkable::TimeValidityError;
    use tor_llcrypto::pk::{curve25519, ed25519::Ed25519PublicKey, rsa};
    use super::*;
    const TESTDATA: &str = include_str!("../../testdata/routerdesc1.txt");
    const TESTDATA2: &str = include_str!("../../testdata/routerdesc2.txt");
    // Generated with a patched C tor to include "happy family" IDs.
    const TESTDATA3: &str = include_str!("../../testdata/routerdesc3.txt");
    fn read_bad(fname: &str) -> String {
        use std::fs;
        use std::path::PathBuf;
        let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        path.push("testdata");
        path.push("bad-routerdesc");
        path.push(fname);
        fs::read_to_string(path).unwrap()
    }
    #[test]
    fn parse_arbitrary() -> Result<()> {
        use std::str::FromStr;
        use tor_checkable::{SelfSigned, TimeBound};
        let rd = RouterDesc::parse(TESTDATA)?
            .check_signature()?
            .dangerously_assume_timely();
        assert_eq!(rd.router.nickname.as_str(), "Akka");
        assert_eq!(rd.router.orport, 443);
        assert_eq!(rd.router.dirport, 0);
        assert_eq!(rd.uptime, Some(1036923));
        assert_eq!(
            rd.family.as_ref(),
            &RelayFamily::from_str(
                "$303509ab910ef207b7438c27435c4a2fd579f1b1 \
                 $56927e61b51e6f363fb55498150a6ddfcf7077f2"
            )
            .unwrap()
        );
        assert_eq!(
            rd.rsa_identity().to_string(),
            "$56927e61b51e6f363fb55498150a6ddfcf7077f2"
        );
        assert_eq!(
            rd.ed_identity().to_string(),
            "CVTjf1oeaL616hH+1+UvYZ8OgkwF3z7UMITvJzm5r7A"
        );
        assert_eq!(
            rd.protocols().to_string(),
            "Cons=1-2 Desc=1-2 DirCache=2 FlowCtrl=1-2 HSDir=2 \
             HSIntro=4-5 HSRend=1-2 Link=1-5 LinkAuth=1,3 Microdesc=1-2 \
             Padding=2 Relay=1-4"
        );
        assert_eq!(
            hex::encode(rd.ntor_onion_key().to_bytes()),
            "329b3b52991613392e35d1a821dd6753e1210458ecc3337f7b7d39bfcf5da273"
        );
        assert_eq!(
            rd.published(),
            humantime::parse_rfc3339("2022-11-14T19:58:52Z").unwrap()
        );
        assert_eq!(
            rd.or_ports().collect::<Vec<_>>(),
            vec![
                "95.216.33.58:443".parse().unwrap(),
                "[2a01:4f9:2a:2145::2]:443".parse().unwrap(),
            ]
        );
        Ok(())
    }
    #[test]
    fn parse_no_tap_key() -> Result<()> {
        use tor_checkable::{SelfSigned, TimeBound};
        let _rd = RouterDesc::parse(TESTDATA2)?
            .check_signature()?
            .dangerously_assume_timely();
        Ok(())
    }
    #[test]
    fn test_bad() {
        use crate::Pos;
        use crate::types::policy::PolicyError;
        fn check(fname: &str, e: &Error) {
            let text = read_bad(fname);
            let rd = RouterDesc::parse(&text);
            assert!(rd.is_err());
            assert_eq!(&rd.err().unwrap(), e);
        }
        check(
            "bad-sig-order",
            &EK::UnexpectedToken
                .with_msg("router-sig-ed25519")
                .at_pos(Pos::from_line(50, 1)),
        );
        check(
            "bad-start1",
            &EK::MisplacedToken
                .with_msg("identity-ed25519")
                .at_pos(Pos::from_line(1, 1)),
        );
        check("bad-start2", &EK::MissingToken.with_msg("identity-ed25519"));
        check(
            "mismatched-fp",
            &EK::BadArgument
                .at_pos(Pos::from_line(12, 1))
                .with_msg("fingerprint does not match RSA identity"),
        );
        check("no-ed-sk", &EK::MissingToken.with_msg("identity-ed25519"));
        check(
            "bad-cc-sign",
            &EK::BadArgument
                .at_pos(Pos::from_line(34, 26))
                .with_msg("not 0 or 1"),
        );
        check(
            "bad-ipv6policy",
            &EK::BadPolicy
                .at_pos(Pos::from_line(43, 1))
                .with_source(PolicyError::InvalidPolicy),
        );
        check(
            "no-ed-id-key-in-cert",
            &EK::BadObjectVal
                .at_pos(Pos::from_line(2, 1))
                .with_source(tor_cert::CertError::MissingPubKey),
        );
        check(
            "non-ed-sk-in-cert",
            &EK::BadObjectVal
                .at_pos(Pos::from_line(2, 1))
                .with_msg("wrong type for signing key in cert"),
        );
        check(
            "bad-ed-sk-in-cert",
            &EK::BadObjectVal
                .at_pos(Pos::from_line(2, 1))
                .with_msg("invalid ed25519 signing key"),
        );
        check(
            "mismatched-ed-sk-in-cert",
            &EK::BadObjectVal
                .at_pos(Pos::from_line(8, 1))
                .with_msg("master-key-ed25519 does not match key in identity-ed25519"),
        );
    }
    #[test]
    fn parse_multiple_annotated() {
        use crate::AllowAnnotations;
        let mut s = read_bad("bad-cc-sign");
        s += "\
@uploaded-at 2020-09-26 18:15:41
@source \"127.0.0.1\"
";
        s += TESTDATA;
        s += "\
@uploaded-at 2020-09-26 18:15:41
@source \"127.0.0.1\"
";
        s += &read_bad("mismatched-fp");
        let rd = RouterReader::new(&s, &AllowAnnotations::AnnotationsAllowed).unwrap();
        let v: Vec<_> = rd.collect();
        assert!(v[0].is_err());
        assert!(v[1].is_ok());
        assert_eq!(
            v[1].as_ref().unwrap().ann.source,
            Some("\"127.0.0.1\"".to_string())
        );
        assert!(v[2].is_err());
    }
    #[test]
    fn test_platform() {
        let tests = [
            // Test with platform.
            (
                "Tor 0.4.4.4-alpha on a flying bison",
                RelayPlatform::Tor(
                    "0.4.4.4-alpha".parse().unwrap(),
                    Some("a flying bison".to_string()),
                ),
            ),
            // Test without platform but potentially weird spacing.
            (
                "Tor 0.4.4.4-alpha on",
                RelayPlatform::Tor("0.4.4.4-alpha".parse().unwrap(), None),
            ),
            (
                "Tor 0.4.4.4-alpha ",
                RelayPlatform::Tor("0.4.4.4-alpha".parse().unwrap(), None),
            ),
            (
                "Tor 0.4.4.4-alpha",
                RelayPlatform::Tor("0.4.4.4-alpha".parse().unwrap(), None),
            ),
            // Test other.
            ("arti 0.0.0", RelayPlatform::Other("arti 0.0.0".to_string())),
        ];
        for (input, output) in tests {
            assert_eq!(input.parse::<RelayPlatform>().unwrap(), output);
            // Round-trip test with input stripped of " on" suffix and trimmed.
            // Otherwise we cannot really make this work because certain inputs
            // contain redundant data on purpose.
            let input = input.strip_suffix(" on").unwrap_or(input);
            let input = input.trim();
            assert_eq!(output.to_string(), input);
        }
    }
    #[test]
    fn test_family_ids() -> Result<()> {
        use tor_checkable::{SelfSigned, TimeBound};
        let rd = RouterDesc::parse(TESTDATA3)?
            .check_signature()?
            .dangerously_assume_timely();
        assert_eq!(
            rd.family_ids().as_ref(),
            &[
                "ed25519:7sToQRuge1bU2hS0CG0ViMndc4m82JhO4B4kdrQey80"
                    .parse()
                    .unwrap(),
                "ed25519:szHUS3ItRd9uk85b1UVnOZx1gg4B0266jCpbuIMNjcM"
                    .parse()
                    .unwrap(),
            ]
        );
        Ok(())
    }
    /// Simple decoding and round-trip encoding for "normal" router descriptors.
    ///
    /// In other words: No edge cases and such.
    #[test]
    fn test_parse2_simple() {
        let input = ParseInput::new(
            include_str!("../../testdata2/cached-descriptors.new"),
            "cached-descriptors.new",
        );
        let rd = parse2::parse_netdoc_multiple::<RouterDescUnverified>(&input)
            .unwrap()
            .into_iter()
            .map(|rd| {
                rd.clone().verify().unwrap();
                rd.unwrap_unverified()
            })
            .map(|(body, sig)| (body, sig.sigs))
            .collect::<Vec<(RouterDesc, RouterDescSignatures)>>();
        assert_eq!(rd.len(), 20);
        assert_eq!(
            rd[0].0.router,
            RouterDescIntroItem {
                dirport: 7102,
                ..RouterDescIntroItemConstructor {
                    nickname: "test002a".parse().unwrap(),
                    address: net::Ipv4Addr::LOCALHOST,
                    orport: 5102,
                }
                .construct()
            }
        );
        assert_eq!(
            rd[0].0.fingerprint.unwrap(),
            "257D 06F0 360B B224 6388 724F 109E C089 5A1D 41FB"
                .parse()
                .unwrap()
        );
        // Round-trip encoding by verifying that decoding it equals the original.
        // This is the best we can get as the current encoder is not bug for
        // bug compatible with the CTor one (i.e. absence of TAP and different
        // order), so this is the closest we can get.
        //
        // Unfortunately, we cannot verify the re-encoded signatures, because
        // the re-encoded body misses the TAP related fields which are accounted
        // for in the signature however.
        let mut out = NetdocEncoder::new();
        for (body, sig) in &rd {
            body.encode_unsigned(&mut out).unwrap();
            sig.encode_unsigned(&mut out).unwrap();
        }
        let out = out.finish().unwrap();
        let input2 = ParseInput::new(out.as_str(), "<router descriptor encoding>");
        let rd2 = parse2::parse_netdoc_multiple::<RouterDescUnverified>(&input2)
            .unwrap()
            .into_iter()
            .map(|rd| rd.unwrap_unverified())
            .map(|(body, sig)| (body, sig.sigs))
            .collect::<Vec<(RouterDesc, _)>>();
        assert_eq!(rd, rd2);
    }
    /// Test for various succeeding and failing verifications.
    #[test]
    fn test_verify() {
        // Generate keys we will use later.
        let rng = &mut testing_rng();
        let rsa_id = rsa::KeyPair::generate(rng).unwrap();
        let ed25519_id = ed25519::Keypair::generate(rng);
        let ed25519_sign = ed25519::Keypair::generate(rng);
        let curve25519_ntor = curve25519::StaticSecret::random_from_rng(&mut *rng);
        let curve25519_ntor = curve25519::StaticKeypair {
            secret: curve25519_ntor.clone(),
            public: (&curve25519_ntor).into(),
        };
        let ed25519_ntor = tor_llcrypto::pk::keymanip::convert_curve25519_to_ed25519_private(
            &curve25519_ntor.secret,
        )
        .unwrap();
        // Values arbitrarily chosen for expirations.
        let too_early = Iso8601TimeSp::from_str("2026-05-01 03:00:00").unwrap().0;
        let published = Iso8601TimeSp::from_str("2026-06-01 03:00:00").unwrap().0;
        let now = Iso8601TimeSp::from_str("2026-06-10 15:30:12").unwrap().0;
        let expiration = Iso8601TimeSp::from_str("2026-06-20 03:00:00").unwrap().0;
        let expired = expiration + Duration::from_secs(60 * 60 * 24);
        // Very boilerplatey construction of a router descriptor.
        // TODO: Probably best to use constructor logic here.
        let mut rd = RouterDesc {
            router: RouterDescIntroItemConstructor {
                nickname: "foo".parse().unwrap(),
                address: Ipv4Addr::LOCALHOST,
                orport: 9000,
            }
            .construct(),
            identity_ed25519: Ed25519IdentityCert::new_signed(
                &ed25519_id,
                Ed25519Identity::from(ed25519_sign.public_key()),
                expiration,
            )
            .unwrap(),
            master_key_ed25519: Ed25519Identity::from(ed25519_id.public_key()).into(),
            bandwidth: BandwidthConstructor {
                average: 0,
                burst: 0,
                observed: 0,
            }
            .construct(),
            platform: None,
            published: published.into(),
            fingerprint: Some(rsa_id.to_public_key().to_rsa_identity().into()),
            hibernating: NumericBoolean(false),
            uptime: None,
            ntor_onion_key: Curve25519Public(curve25519_ntor.public),
            ntor_onion_key_crosscert: NtorOnionKeyCrossCertConstructor {
                bit: NumericBoolean(ed25519_ntor.1 == 1),
                cert: Ed25519NtorCrossCert::new_signed(
                    &ed25519_ntor.0,
                    ed25519_id.public_key().into(),
                    expiration,
                )
                .unwrap(),
            }
            .construct(),
            signing_key: rsa_id.to_public_key(),
            ipv4_policy: AddrPolicy::default(),
            ipv6_policy: Default::default(),
            overload_general: None,
            contact: None,
            family: Default::default(),
            family_cert: Default::default(),
            caches_extra_info: Some(Default::default()),
            extra_info_digest: None,
            hidden_service_dir: Some(Default::default()),
            or_address: Default::default(),
            tunnelled_dir_server: Some(Default::default()),
            proto: tor_protover::Protocols::new(),
            __non_exhaustive: (),
        };
        let rd_original = rd.clone();
        let verify =
            |rd: &RouterDesc| -> std::result::Result<TimeRangeBound<RouterDesc>, VerifyFailed> {
                let encoded = rd.encode_sign(&rsa_id, &ed25519_sign).unwrap();
                let decoded = parse2::parse_netdoc::<RouterDescUnverified>(&ParseInput::new(
                    &encoded,
                    "<test_invalid>",
                ))
                .unwrap();
                decoded.verify()
            };
        // Test valid and invalid timestamps.
        assert_eq!(
            verify(&rd).unwrap().if_valid_at(&too_early).unwrap_err(),
            TimeValidityError::NotYetValid(published.duration_since(too_early).unwrap())
        );
        verify(&rd).unwrap().if_valid_at(&published).unwrap();
        // This is our good/"everything is working" test
        verify(&rd).unwrap().if_valid_at(&now).unwrap();
        verify(&rd).unwrap().if_valid_at(&expiration).unwrap();
        assert_eq!(
            verify(&rd).unwrap().if_valid_at(&expired).unwrap_err(),
            TimeValidityError::Expired(expired.duration_since(expiration).unwrap())
        );
        // Let's make the certificate inconsistent by changing the master key.
        rd.master_key_ed25519 = Ed25519Public(Ed25519Identity::from([0x12; 32]));
        assert_eq!(verify(&rd).unwrap_err(), VerifyFailed::Inconsistent);
        rd = rd_original.clone();
        // Set the published to expired (weird on many levels).
        rd.published = expired.into();
        assert_eq!(
            verify(&rd).unwrap().if_valid_at(&now).unwrap_err(),
            TimeValidityError::NotYetValid(expired.duration_since(now).unwrap())
        );
        rd = rd_original.clone();
        // Have an inconsistent fingerprint.
        let other_rsa_key = rsa::KeyPair::generate(rng).unwrap();
        rd.fingerprint = Some(other_rsa_key.to_public_key().to_rsa_identity().into());
        assert_eq!(verify(&rd).unwrap_err(), VerifyFailed::Inconsistent);
        // Encoding rejects a signing key that does not match the set keypair.
        rd.signing_key = other_rsa_key.to_public_key();
        assert!(rd.encode_sign(&rsa_id, &ed25519_sign).is_err());
        // It should work again if we set it to None.
        rd.signing_key = rd_original.signing_key.clone();
        rd.fingerprint = None;
        verify(&rd).unwrap().if_valid_at(&now).unwrap();
        rd = rd_original.clone();
        // If we change the ntor-onion-key, the crosscert verification will fail.
        // We must generate a valid random key here because otherwise, decompress
        // will fail.
        let other_curve25519 = curve25519::StaticSecret::random_from_rng(&mut *rng);
        let other_curve25519 = (&other_curve25519).into();
        rd.ntor_onion_key = Curve25519Public(other_curve25519);
        assert_eq!(verify(&rd).unwrap_err(), VerifyFailed::VerifyFailed);
        rd = rd_original.clone();
        // Testing for a signature key of the wrong size is hard because
        // tor-llcrypto makes it purposely hard to generate key sizes other
        // than 1024 bit.
        // TODO: Test family certificates.
        // Encoding rejects a certificate for a different Ed25519 signing key.
        let different_ed25519_sign = ed25519::Keypair::generate(rng);
        rd.identity_ed25519 = Ed25519IdentityCert::new_signed(
            &ed25519_id,
            Ed25519Identity::from(different_ed25519_sign.public_key()),
            expiration,
        )
        .unwrap();
        assert!(rd.encode_sign(&rsa_id, &ed25519_sign).is_err());
        rd = rd_original.clone();
        // Encoding rejects a different RSA key even without a fingerprint.
        let different_rsa_id = rsa::KeyPair::generate(rng).unwrap();
        rd.signing_key = different_rsa_id.to_public_key();
        rd.fingerprint = None;
        assert!(rd.encode_sign(&rsa_id, &ed25519_sign).is_err());
    }
    #[test]
    fn ntor_onion_key_cross_cert() {
        // Dummy helper for parsing a subset of a router desc.
        #[derive(Debug, Deftly)]
        #[derive_deftly(NetdocParseable)]
        #[allow(unused)]
        struct TestDoc {
            /// Intro item.
            router: RouterDescIntroItem,
            /// Timestamp used for `now` in certificate validation.
            #[deftly(netdoc(single_arg))]
            published: Iso8601TimeSp,
            /// Required to ensure certified key of the crosscert.
            #[deftly(netdoc(single_arg))]
            master_key_ed25519: Ed25519Public,
            /// Required to obtain the key signing the crosscert.
            #[deftly(netdoc(single_arg))]
            ntor_onion_key: Curve25519Public,
            /// The actual crosscert.
            ntor_onion_key_crosscert: NtorOnionKeyCrossCert,
        }
        impl TestDoc {
            // Quick verify helper.
            fn verify(&self, now: time::SystemTime) {
                Ed25519NtorCrossCert::verify(
                    // Converts X25519 to Ed25519.
                    tor_llcrypto::pk::keymanip::convert_curve25519_to_ed25519_public(
                        &self.ntor_onion_key.0,
                        self.ntor_onion_key_crosscert.bit.0.into(),
                    )
                    .unwrap()
                    .into(),
                    self.master_key_ed25519.0,
                    self.ntor_onion_key_crosscert.cert.raw_unverified().clone(),
                )
                .unwrap()
                .check_valid_at(&now)
                .unwrap();
            }
        }
        let descs = include_str!("../../testdata2/cached-descriptors.new");
        let descs = parse2::parse_netdoc_multiple::<TestDoc>(&ParseInput::new(
            descs,
            "cached-descriptors.new",
        ))
        .unwrap();
        // Find the first with negative and first with positive X coordinate.
        let negative_rd = descs
            .iter()
            .find(|rd| rd.ntor_onion_key_crosscert.bit.0)
            .unwrap();
        let positive_rd = descs
            .iter()
            .find(|rd| !rd.ntor_onion_key_crosscert.bit.0)
            .unwrap();
        negative_rd.verify(negative_rd.published.0);
        positive_rd.verify(positive_rd.published.0);
    }
}