1
//! Types used to parse arguments of entries in a directory document.
2
//!
3
//! There are some types that are pretty common, like "ISOTime",
4
//! "base64-encoded data", and so on.
5

            
6
pub use b16impl::*;
7
pub use b64impl::*;
8
pub use contact_info::*;
9
pub use curve25519impl::*;
10
pub use ed25519impl::*;
11
pub use edcert::*;
12
pub use fingerprint::*;
13
pub use hostname::*;
14
pub use rsa::*;
15
pub use timeimpl::*;
16

            
17
pub use nickname::{InvalidNickname, Nickname};
18

            
19
pub use boolean::NumericBoolean;
20

            
21
pub use fingerprint::{Base64Fingerprint, Fingerprint};
22

            
23
pub use identified_digest::{DigestName, IdentifiedDigest};
24

            
25
pub use ignored_impl::{
26
    Ignored, IgnoredItemOrObjectValue, ItemPresent, NoMoreArguments, NotPresent,
27
    NotPresentEachValue,
28
};
29

            
30
use crate::NormalItemArgument;
31
use crate::encode::{
32
    self,
33
    ItemArgument,
34
    ItemEncoder,
35
    ItemObjectEncodable,
36
    ItemValueEncodable,
37
    // `E` for "encode`; different from `parse2::MultiplicitySelector`
38
    MultiplicitySelector as EMultiplicitySelector,
39
    NetdocEncoder,
40
};
41
use crate::parse2::{
42
    self, ArgumentError, ArgumentStream, ItemArgumentParseable, ItemObjectParseable,
43
    ItemValueParseable, SignatureHashInputs, UnparsedItem,
44
    multiplicity::{
45
        ArgumentSetMethods,
46
        ItemSetMethods,
47
        // `P2` for "parse2`; different from `encode::MultiplicitySelector`
48
        MultiplicitySelector as P2MultiplicitySelector,
49
        ObjectSetMethods,
50
    },
51
    sig_hashes::Sha1WholeKeywordLine,
52
};
53

            
54
use derive_deftly::{Deftly, define_derive_deftly, define_derive_deftly_module};
55
use digest::Digest as _;
56
use educe::Educe;
57
use std::cmp::{self, Ordering, PartialOrd};
58
use std::fmt::{self, Display};
59
use std::iter;
60
use std::marker::PhantomData;
61
use std::ops::{Deref, DerefMut};
62
use std::result::Result as StdResult;
63
use std::str::FromStr;
64
use subtle::{Choice, ConstantTimeEq};
65
use tor_error::{Bug, ErrorReport as _, internal, into_internal};
66
use void::{ResultVoidExt as _, Void};
67

            
68
/// Describes a value that van be decoded from a bunch of bytes.
69
///
70
/// Used for decoding the objects between BEGIN and END tags.
71
pub(crate) trait FromBytes: Sized {
72
    /// Try to parse a value of this type from a byte slice
73
    fn from_bytes(b: &[u8], p: crate::Pos) -> crate::Result<Self>;
74
    /// Try to parse a value of this type from a vector of bytes,
75
    /// and consume that value
76
3016
    fn from_vec(v: Vec<u8>, p: crate::Pos) -> crate::Result<Self> {
77
3016
        Self::from_bytes(&v[..], p)
78
3016
    }
79
}
80

            
81
define_derive_deftly_module! {
82
    /// Implement conversion traits for a transparent newtype around bytes - shared code
83
    ///
84
    /// This is precisely `#[derive_deftly(Transparent)]`, but in the form of a deftly module,
85
    /// so that other derives (eg `BytesTransparent`) can re-use it.
86
    Transparent beta_deftly:
87

            
88
    // Expands to bullet points for "generated code", except omitting
89
    // `AsRef` & `AsMut` because some uses sites have additional impls of those,
90
    // which are best presented together in the docs.
91
  ${define TRANSPARENT_DOCS_IMPLS {
92
    ///  * impls of `Deref`, `DerefMut`
93
    ///  * impls of `From<field>` and "`Into`" (technically, `From<Self> for field`)
94
  }}
95

            
96
    // Expands to the implementations
97
  ${define TRANSPARENT_IMPLS {
98

            
99
  ${for fields {
100
    ${loop_exactly_1 "must be applied to a single-field struct"}
101

            
102
    ${impl From<$ftype>} {
103
987614
        fn from($fpatname: $ftype) -> $ttype {
104
            $vpat
105
        }
106
    }
107

            
108
    // TODO: This implementation is probably a bug, as it forbids to derive
109
    // Transparent on types like `struct Foo<T>(T)`, namely `T` not being
110
    // covered by something else, like `PhantomData<T>` or `Vec<T>`.
111
    ${impl From<$ttype> for $ftype} {
112
50716
        fn from(self_: $ttype) -> $ftype {
113
            self_.$fname
114
        }
115
    }
116

            
117
    ${impl Deref} {
118
        type Target = $ftype;
119
322285561
        fn deref(&self) -> &$ftype {
120
            &self.$fname
121
        }
122
    }
123

            
124
    ${impl DerefMut} {
125
1
        fn deref_mut(&mut self) -> &mut $ftype {
126
            &mut self.$fname
127
        }
128
    }
129

            
130
    ${impl AsRef<$ftype>} {
131
        fn as_ref(&self) -> &$ftype {
132
            &self.$fname
133
        }
134
    }
135

            
136
    ${impl AsMut<$ftype>} {
137
        fn as_mut(&mut self) -> &mut $ftype {
138
            &mut self.$fname
139
        }
140
    }
141
  }}
142
  }}
143
}
144

            
145
define_derive_deftly! {
146
    use Transparent;
147

            
148
    /// Implement conversion traits for an arbitrary transparent newtype
149
    ///
150
    /// # Requirements
151
    ///
152
    ///  * Self should be a single-field struct
153
    ///  * Self should have no runtime invariants
154
    ///
155
    /// # Generated code
156
    ///
157
    $TRANSPARENT_DOCS_IMPLS
158
    ///  * impls of `AsMut<field>`, `AsRef<field>`
159
    ///
160
    /// # Guidelines
161
    ///
162
    ///  * the field should be `pub`, with `#[allow(clippy::exhaustive_structs)]`
163
    ///  * derive `Hash`, `Debug` and (usually) `Clone`
164
    ///  * consider deriving `PartialEq` and `Eq`
165
    ///    but for types containing bytes, use [`ConstantTimeEq`],
166
    ///    eg with [`#[derive_deftly(BytesTransparent)]`](derive_deftly_template_BytesTransparent)
167
    ///    (instead of `Transparent`).
168
    ///  * implement `FromStr`, `Display`, `NormalItemArgument`, as required
169
    Transparent for struct, beta_deftly:
170

            
171
    $TRANSPARENT_IMPLS
172
}
173

            
174
define_derive_deftly! {
175
    use Transparent;
176

            
177
    /// Implement `ConstantTimeEq`, `.as_bytes()`, etc., for a transparent newtype around bytes
178
    ///
179
    /// # Requirements
180
    ///
181
    ///  * Self should be a single-field struct
182
    ///  * Self should deref to `&[u8]` (and to `&mut [u8]`).
183
    ///  * (so Self should have no runtime invariants)
184
    ///
185
    /// # Generated code
186
    ///
187
    ///  * impls of `ConstantTimeEq`, `Eq`, `PartialEq`, `Ord`, `PartialOrd`
188
    ///  * `as_bytes()` method
189
    ${TRANSPARENT_DOCS_IMPLS}
190
    ///  * impls of `AsMut<field>`, `AsRef<field>`, `AsRef<[u8]>`, `AsMut<[u8]>`
191
    ///
192
    // We could derive Debug here but then we have to deal with the Fixed's N
193
    // which gets quite fiddly.
194
    //
195
    /// # Guidelines
196
    ///
197
    ///  * derive `Hash` and write `#[allow(clippy::derived_hash_with_manual_eq)]`
198
    ///  * impl `FromStr` and `Display` (if required, which they usually will be)
199
    ///  * derive `derive_more::Debug` eg with `#[debug(r#"B64("{self}")"#)]`
200
    ///  * `impl NormalItemArgument` if appropriate (ie the representation has no spaces)
201
    BytesTransparent for struct, beta_deftly:
202

            
203
    $TRANSPARENT_IMPLS
204

            
205
    ${impl ConstantTimeEq} {
206
40
        fn ct_eq(&self, other: &$ttype) -> Choice {
207
          $(
208
40
            self.$fname.ct_eq(&other.$fname)
209
          )
210
40
        }
211
    }
212
    $/// `$tname` is `Eq` via its constant-time implementation.
213
    ${impl PartialEq} {
214
40
        fn eq(&self, other: &$ttype) -> bool {
215
40
            self.ct_eq(other).into()
216
40
        }
217
    }
218
    ${impl Eq} {}
219
    ${impl PartialOrd} {
220
        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
221
            Some(self.cmp(other))
222
        }
223
    }
224
    ${impl Ord} {
225
        fn cmp(&self, other: &Self) -> Ordering {
226
          $(
227
            self.$fname.cmp(&other.$fname)
228
          )
229
        }
230
    }
231

            
232
    $impl {
233
        /// Return the byte array from this object.
234
231660
        pub fn as_bytes(&self) -> &[u8] {
235
          $(
236
231660
            &self.$fname[..]
237
          )
238
231660
        }
239
    }
240

            
241
    ${impl AsRef<[u8]>} {
242
        fn as_ref(&self) -> &[u8] {
243
          $(
244
            self.$fname.as_ref()
245
          )
246
        }
247
    }
248

            
249
    ${impl AsMut<[u8]>} {
250
        fn as_mut(&mut self) -> &mut [u8] {
251
          $(
252
            self.$fname.as_mut()
253
          )
254
        }
255
    }
256
}
257

            
258
/// Types for decoding base64-encoded values.
259
mod b64impl {
260
    use super::*;
261
    use crate::{Error, NetdocErrorKind as EK, Pos, Result};
262
    use base64ct::{Base64, Base64Unpadded, Encoding};
263
    use std::ops::RangeBounds;
264

            
265
    /// A byte array, encoded in base64 with optional padding.
266
    ///
267
    /// On output (`Display`), output is unpadded.
268
    #[derive(Clone, Hash, Deftly)]
269
    #[derive_deftly(BytesTransparent)]
270
    #[allow(clippy::derived_hash_with_manual_eq)]
271
    #[derive(derive_more::Debug)]
272
    #[debug(r#"B64("{self}")"#)]
273
    #[allow(clippy::exhaustive_structs)]
274
    pub struct B64(pub Vec<u8>);
275

            
276
    impl FromStr for B64 {
277
        type Err = Error;
278
577889
        fn from_str(s: &str) -> Result<Self> {
279
577889
            let v: core::result::Result<Vec<u8>, base64ct::Error> = match s.len() % 4 {
280
12483
                0 => Base64::decode_vec(s),
281
565406
                _ => Base64Unpadded::decode_vec(s),
282
            };
283
577889
            let v = v.map_err(|_| {
284
18007
                EK::BadArgument
285
18007
                    .with_msg("Invalid base64")
286
18007
                    .at_pos(Pos::at(s))
287
18007
            })?;
288
559882
            Ok(B64(v))
289
577889
        }
290
    }
291

            
292
    impl Display for B64 {
293
2658
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
294
2658
            Display::fmt(&Base64Unpadded::encode_string(&self.0), f)
295
2658
        }
296
    }
297

            
298
    impl B64 {
299
        /// Return this object if its length is within the provided bounds
300
        /// object, or an error otherwise.
301
2050
        pub(crate) fn check_len<B: RangeBounds<usize>>(self, bounds: B) -> Result<Self> {
302
2050
            if bounds.contains(&self.0.len()) {
303
2049
                Ok(self)
304
            } else {
305
1
                Err(EK::BadObjectVal.with_msg("Invalid length on base64 data"))
306
            }
307
2050
        }
308

            
309
        /// Try to convert this object into an array of N bytes.
310
        ///
311
        /// Return an error if the length is wrong.
312
41438
        pub(crate) fn into_array<const N: usize>(self) -> Result<[u8; N]> {
313
41438
            self.0
314
41438
                .try_into()
315
41438
                .map_err(|_| EK::BadObjectVal.with_msg("Invalid length on base64 data"))
316
41438
        }
317
    }
318

            
319
    impl FromIterator<u8> for B64 {
320
        fn from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Self {
321
            Self(iter.into_iter().collect())
322
        }
323
    }
324

            
325
    impl NormalItemArgument for B64 {}
326

            
327
    /// A byte array encoded in base64 with a fixed length.
328
    ///
329
    /// As with [`B64`], padding is optional when parsing, but omitted when encoding.
330
    #[derive(Clone, Hash, Deftly)]
331
    #[derive_deftly(BytesTransparent)]
332
    #[allow(clippy::derived_hash_with_manual_eq)]
333
    #[derive(derive_more::Debug)]
334
    #[debug(r#"FixedB64::<{N}>("{self}")"#)]
335
    #[allow(clippy::exhaustive_structs)]
336
    pub struct FixedB64<const N: usize>(pub [u8; N]);
337

            
338
    impl<const N: usize> Display for FixedB64<N> {
339
2620
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340
2620
            Display::fmt(&B64(self.0.to_vec()), f)
341
2620
        }
342
    }
343

            
344
    impl<const N: usize> FromStr for FixedB64<N> {
345
        type Err = Error;
346
287694
        fn from_str(s: &str) -> Result<Self> {
347
287694
            Ok(Self(B64::from_str(s)?.0.try_into().map_err(|_| {
348
1
                EK::BadArgument
349
1
                    .at_pos(Pos::at(s))
350
1
                    .with_msg("invalid length")
351
1
            })?))
352
287694
        }
353
    }
354

            
355
    impl<const N: usize> NormalItemArgument for FixedB64<N> {}
356
}
357

            
358
// ============================================================
359

            
360
/// Types for decoding hex-encoded values.
361
mod b16impl {
362
    use super::*;
363
    use crate::{Error, NetdocErrorKind as EK, Pos, Result};
364

            
365
    /// A byte array encoded in hexadecimal; prints in lowercase
366
    ///
367
    /// Both uppercase and lowercase are tolerated when parsing.
368
    #[derive(Clone, Hash, Deftly)]
369
    #[derive_deftly(BytesTransparent)]
370
    #[allow(clippy::derived_hash_with_manual_eq)]
371
    #[derive(derive_more::Debug)]
372
    #[debug(r#"B16("{self}")"#)]
373
    #[allow(clippy::exhaustive_structs)]
374
    pub struct B16(pub Vec<u8>);
375

            
376
    /// A byte array encoded in hexadecimal; prints in uppercase
377
    ///
378
    /// Both uppercase and lowercase are tolerated when parsing.
379
    #[derive(Clone, Hash, Deftly)]
380
    #[derive_deftly(BytesTransparent)]
381
    #[allow(clippy::derived_hash_with_manual_eq)]
382
    #[derive(derive_more::Debug)]
383
    #[debug(r#"B16U("{self}")"#)]
384
    #[allow(clippy::exhaustive_structs)]
385
    pub struct B16U(pub Vec<u8>);
386

            
387
    /// A fixed-length version of [`B16U`].
388
    #[derive(Clone, Hash, Deftly)]
389
    #[derive_deftly(BytesTransparent)]
390
    #[allow(clippy::derived_hash_with_manual_eq)]
391
    #[derive(derive_more::Debug)]
392
    #[debug(r#"FixedB16U("{self}")"#)]
393
    #[allow(clippy::exhaustive_structs)]
394
    pub struct FixedB16U<const N: usize>(pub [u8; N]);
395

            
396
    impl FromStr for B16 {
397
        type Err = Error;
398
140385
        fn from_str(s: &str) -> Result<Self> {
399
140385
            let bytes = hex::decode(s).map_err(|_| {
400
2
                EK::BadArgument
401
2
                    .at_pos(Pos::at(s))
402
2
                    .with_msg("invalid hexadecimal")
403
2
            })?;
404
140383
            Ok(B16(bytes))
405
140385
        }
406
    }
407

            
408
    impl FromStr for B16U {
409
        type Err = Error;
410
140380
        fn from_str(s: &str) -> Result<Self> {
411
140380
            Ok(B16U(B16::from_str(s)?.0))
412
140380
        }
413
    }
414

            
415
    impl<const N: usize> FromStr for FixedB16U<N> {
416
        type Err = Error;
417
12573
        fn from_str(s: &str) -> Result<Self> {
418
12573
            Ok(Self(B16U::from_str(s)?.0.try_into().map_err(|_| {
419
                EK::BadArgument
420
                    .at_pos(Pos::at(s))
421
                    .with_msg("invalid length")
422
            })?))
423
12573
        }
424
    }
425

            
426
    /// Write `b` to `f` in hex uppercase
427
    // `hex` has `hex::encode_upper` but that allocates a `String`
428
75
    fn write_b16u(b: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
429
1454
        for c in b {
430
1454
            write!(f, "{c:02X}")?;
431
        }
432
75
        Ok(())
433
75
    }
434

            
435
    impl Display for B16 {
436
3
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
437
            // `hex` has `hex::encode` but that allocates a `String`, which this approach doesn't
438
14
            for c in self.as_bytes() {
439
14
                write!(f, "{c:02x}")?;
440
            }
441
3
            Ok(())
442
3
        }
443
    }
444

            
445
    impl Display for B16U {
446
55
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
447
55
            write_b16u(self.as_bytes(), f)
448
55
        }
449
    }
450

            
451
    impl<const N: usize> Display for FixedB16U<N> {
452
20
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453
20
            write_b16u(self.as_bytes(), f)
454
20
        }
455
    }
456

            
457
    impl NormalItemArgument for B16 {}
458
    impl NormalItemArgument for B16U {}
459
    impl<const N: usize> NormalItemArgument for FixedB16U<N> {}
460
}
461

            
462
// ============================================================
463

            
464
/// Types for decoding curve25519 keys
465
mod curve25519impl {
466
    use super::*;
467

            
468
    use crate::{Error, NormalItemArgument, Result, types::misc::FixedB64};
469
    use tor_llcrypto::pk::curve25519::PublicKey;
470

            
471
    /// A Curve25519 public key, encoded in base64 with optional padding
472
    #[derive(Debug, Clone, PartialEq, Eq, Hash, Deftly)]
473
    // Sadly not Ord because x25519_dalek::PublicKey isn't
474
    #[derive_deftly(Transparent)]
475
    #[allow(clippy::exhaustive_structs)]
476
    pub struct Curve25519Public(pub PublicKey);
477

            
478
    impl FromStr for Curve25519Public {
479
        type Err = Error;
480
20170
        fn from_str(s: &str) -> Result<Self> {
481
20170
            let pk: FixedB64<32> = s.parse()?;
482
20165
            let pk: [u8; 32] = pk.into();
483
20165
            Ok(Curve25519Public(pk.into()))
484
20170
        }
485
    }
486

            
487
    impl Display for Curve25519Public {
488
832
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
489
832
            FixedB64::from(self.0.to_bytes()).fmt(f)
490
832
        }
491
    }
492

            
493
    impl NormalItemArgument for Curve25519Public {}
494
}
495

            
496
// ============================================================
497

            
498
/// Types for decoding ed25519 keys
499
mod ed25519impl {
500
    use super::*;
501

            
502
    use crate::{Error, NormalItemArgument, Result, types::misc::FixedB64};
503
    use derive_deftly::Deftly;
504
    use tor_llcrypto::pk::ed25519::{Ed25519Identity, Signature};
505

            
506
    /// An alleged ed25519 public key, encoded in base64 with optional
507
    /// padding.
508
    #[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, Deftly)]
509
    #[derive_deftly(Transparent)]
510
    #[allow(clippy::exhaustive_structs)]
511
    pub struct Ed25519Public(pub Ed25519Identity);
512

            
513
    impl FromStr for Ed25519Public {
514
        type Err = Error;
515
20469
        fn from_str(s: &str) -> Result<Self> {
516
20469
            let pk: FixedB64<32> = s.parse()?;
517
20466
            Ok(Ed25519Public(Ed25519Identity::new(pk.into())))
518
20469
        }
519
    }
520

            
521
    impl Display for Ed25519Public {
522
1109
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
523
1109
            let pk: [u8; 32] = self.0.into();
524
1109
            let pk = FixedB64::from(pk);
525
1109
            pk.fmt(f)
526
1109
        }
527
    }
528

            
529
    impl NormalItemArgument for Ed25519Public {}
530

            
531
    /// Helper that checks for the presence of `ed25519`.
532
    #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)] //
533
    #[derive(derive_more::Display, derive_more::FromStr)]
534
    #[display(rename_all = "lowercase")]
535
    #[from_str(rename_all = "lowercase")]
536
    #[allow(clippy::exhaustive_enums)]
537
    pub enum Ed25519AlgorithmString {
538
        /// Ed25519 encoded as `ed25519`.
539
        Ed25519,
540
    }
541

            
542
    impl NormalItemArgument for Ed25519AlgorithmString {}
543

            
544
    /// Ed25519 public key in the form `<keyword> id <base64>`
545
    ///
546
    ///  * `id` in microdescriptors:
547
    ///    <https://spec.torproject.org/dir-spec/computing-microdescriptors.html>
548
    ///
549
    ///  * `identity-ed25519` in routerdescs:
550
    ///    <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:identity-ed25519>
551
    ///
552
    ///  * `id` in votes' routerstatus entries:
553
    ///    <https://spec.torproject.org/dir-spec/consensus-formats.html#item:id>
554
    #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, Deftly)]
555
    #[derive_deftly(ItemValueEncodable, ItemValueParseable)]
556
    #[non_exhaustive]
557
    pub struct Ed25519IdentityLine {
558
        /// Fixed magic identifier (`ed25519`) for this line.
559
        pub alg: Ed25519AlgorithmString,
560

            
561
        /// The actual Ed25519 identity.
562
        pub pk: Ed25519Public,
563
    }
564

            
565
    impl From<Ed25519Public> for Ed25519IdentityLine {
566
479918
        fn from(pk: Ed25519Public) -> Self {
567
479918
            Self {
568
479918
                alg: Ed25519AlgorithmString::Ed25519,
569
479918
                pk,
570
479918
            }
571
479918
        }
572
    }
573

            
574
    impl From<Ed25519Identity> for Ed25519IdentityLine {
575
479918
        fn from(pk: Ed25519Identity) -> Self {
576
479918
            Ed25519Public(pk).into()
577
479918
        }
578
    }
579

            
580
    impl ItemArgument for Signature {
581
31
        fn write_arg_onto(&self, out: &mut ItemEncoder) -> StdResult<(), Bug> {
582
31
            FixedB64::from(self.to_bytes()).write_arg_onto(out)
583
31
        }
584
    }
585
}
586

            
587
// ============================================================
588

            
589
/// Dummy types like [`Ignored`]
590
mod ignored_impl {
591
    use super::*;
592

            
593
    use crate::parse2::ErrorProblem as EP;
594
    use ArgumentError as AE;
595

            
596
    /// Part of a network document, that isn't actually there.
597
    ///
598
    /// Used as a standin in `ns_type!` calls in various netstatus `each_variety.rs`.
599
    /// The effect is as if the field were omitted from the containing type.
600
    ///
601
    ///  * When used as item(s) (ie, a field type when deriving `NetdocParseable\[Fields\]`):
602
    ///    **ignores any number** of items with that field's keyword during parsing,
603
    ///    and emits none during encoding.
604
    ///
605
    ///    (To *reject* documents containing this item, use `Option<Void>`,
606
    ///    but note that the spec says unknown items should be ignored,
607
    ///    which would normally include items which are merely missing from one variety.)
608
    ///
609
    ///  * When used as an argument (ie, a field type when deriving `ItemValueParseable`,
610
    ///    or with `netdoc(single_arg)`  when deriving `NetdocParseable\[Fields\]`):
611
    ///    consumes **no arguments** during parsing, and emits none during encoding.
612
    ///
613
    ///  * When used as an object field (ie, `netdoc(object)` when deriving `ItemValueParseable`):
614
    ///    **rejects** an object - failing the parse if one is present.
615
    ///    (Functions similarly to `Option<Void>`, but prefer `NotPresent` as it's clearer.)
616
    ///
617
    ///  * When used as a sub-document (ie, `netdoc(flatten)` when deriving a document trait),
618
    ///    it recognises, and encodes as, no fields.
619
    ///
620
    /// There are bespoke impls of the multiplicity traits
621
    /// `ItemSetMethods` and `ObjectSetMethods`:
622
    /// don't wrap this type in `Option` or `Vec`.
623
    //
624
    // TODO we'll need to implement ItemArgument etc., for encoding, too.
625
    #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Default)]
626
    #[allow(clippy::exhaustive_structs)]
627
    #[derive(Deftly)]
628
    #[derive_deftly(NetdocEncodableFields, NetdocParseableFields)]
629
    pub struct NotPresent;
630

            
631
    /// An individual value that is not present - placeholder type
632
    ///
633
    /// This is the "single" item type for encoding multiplicity
634
    /// (for Items, Arguments or Objects), for [`NotPresent`].
635
    ///
636
    /// It should not be used directly.
637
    ///
638
    /// During parsing, each "not present" item is ignored,
639
    /// but the multiplicity arrangements involve parsing each value
640
    /// and then passing the item value to [`ItemSetMethods::accumulate`]
641
    /// where (for [`NotPresentEachValue`]) it is discarded.
642
    /// Therefore this type must be inhabited; the item parser discards the unparsed item.
643
    ///
644
    /// During parsing of arguments, parsing is driven by
645
    /// [our `ArgumentSetMethods::parse_with`][`P2MultiplicitySelector::<NotPresent>::parse_with)
646
    /// which doesn't need to call any parser.
647
    /// So the [`ItemArgumentParseable`] implementation always throws an error.
648
    ///
649
    /// During parsing of objects, rejection is done by
650
    /// [`NotPresentEachValue::check_label`] (and `from_bytes`).
651
    ///
652
    /// For encoding, there is only one multiplicity system which
653
    /// will never call any encoding function, so the encoding functions all throw `Bug`.
654
    ///
655
    /// This type has a similar role to `IgnoredItemOrObjectValue`,
656
    /// but `NotPresentEachValue` is different in detail,
657
    /// and (unlike `Ignored`) must support arguments, not just items and objects.
658
    #[derive(Debug, Clone, Deftly)]
659
    #[non_exhaustive]
660
    #[derive_deftly(ItemValueParseable, NetdocParseableFields)]
661
    pub struct NotPresentEachValue;
662

            
663
    /// Ignored part of a network document.
664
    ///
665
    /// With `parse2`, can be used as an item, object, or even flattened-fields.
666
    ///
667
    /// When deriving `parse2` traits, and a field is absent in a particular netstatus variety,
668
    /// use `ns_type!` with [`NotPresent`], rather than `Ignored`.
669
    ///
670
    /// During encoding as an Items or Objects, will be entirely omitted,
671
    /// via the multiplicity arrangements.
672
    ///
673
    /// Cannot be encoded as an Argument: if this is not the last
674
    /// Argument, we need something to put into the output document to avoid generating
675
    /// a document with the arguments out of step.  If it *is* the last argument,
676
    /// it could simply be omitted, since additional arguments are in any case ignored.
677
    #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Default, Deftly)]
678
    #[derive_deftly(ItemValueParseable, NetdocParseableFields)]
679
    #[allow(clippy::exhaustive_structs)]
680
    pub struct Ignored;
681

            
682
    /// An Item or Object that would be ignored during parsing and is omitted during encoding
683
    ///
684
    /// This is the "single" item type for encoding multiplicity for Items or Objects,
685
    /// for [`Ignored`].
686
    ///
687
    /// It should not be used directly.
688
    ///
689
    /// This type is uninhabited.
690
    pub struct IgnoredItemOrObjectValue(Void);
691

            
692
    /// Indicates that no further arguments are allowed in a network document Item line
693
    ///
694
    /// Unlike [`NotPresent`], this fails during parsing if there are any more arguments.
695
    ///
696
    /// Should appear only at the end of the argument list.
697
    #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Default)]
698
    #[allow(clippy::exhaustive_structs)]
699
    pub struct NoMoreArguments;
700

            
701
    /// An item that only matters in terms of presence of absence.
702
    ///
703
    /// Useful for items such as `tunnelled-dir-server` where the mere presence
704
    /// implies a truthful value.
705
    ///
706
    /// This wrapper implements [`ItemValueParseable`] and [`ItemValueEncodable`]
707
    /// rejecting all arguments and objects and just expecting/emitting the
708
    /// keyword (or not).
709
    ///
710
    /// # Examples
711
    ///
712
    /// The following shows an except from a hypothetical netdoc with a
713
    /// [`ItemPresent`] item.
714
    ///
715
    /// ```
716
    /// use derive_deftly::Deftly;
717
    /// use tor_netdoc::types::*;
718
    /// use tor_netdoc::parse2::*;
719
    /// use tor_netdoc::*;
720
    ///
721
    /// #[derive(Debug, Default)]
722
    /// struct Hello;
723
    ///
724
    /// #[derive(Deftly, Debug)]
725
    /// #[derive_deftly(NetdocParseable)]
726
    /// struct TestDoc {
727
    ///     intro: Ignored,
728
    ///     hello: Option<ItemPresent<Hello>>,
729
    /// }
730
    ///
731
    /// // hello is not present.
732
    /// let doc = parse_netdoc::<TestDoc>(&ParseInput::new("intro\n", "")).unwrap();
733
    /// assert!(doc.hello.is_none());
734
    ///
735
    /// // hello is present.
736
    /// let doc = parse_netdoc::<TestDoc>(&ParseInput::new("intro\nhello\n", "")).unwrap();
737
    /// assert!(doc.hello.is_some());
738
    ///
739
    /// // hello has arguments which are ignored.
740
    /// let doc = parse_netdoc::<TestDoc>(&ParseInput::new("intro\nhello world\n", "")).unwrap();
741
    /// assert!(doc.hello.is_some());
742
    ///
743
    /// // hello is present twice which is not allowed.
744
    /// let doc = parse_netdoc::<TestDoc>(&ParseInput::new("intro\nhello\nhello\n", "")).unwrap_err();
745
    /// ```
746
    //
747
    // We cannot derive Transparent here, because it is not possible to
748
    // implement `From<ItemPresent<T>> for T` due to orphan rule.
749
    //
750
    // Otherwise, a downstream crate could for example implement
751
    // `From<ItemPresent<U>> for U` with `U` being a locally defined type,
752
    // leading to a conflicting implementation.  A solution would be to cover
753
    // `T` behind another generic type such as `PhantomData`, as this can't be
754
    // a type in a downstream crate, but that level of indirection feels wrong.
755
    #[derive(Debug, Copy, Clone, Default, Ord, PartialOrd, Eq, PartialEq, Hash)]
756
    //
757
    #[derive(
758
        derive_more::From,
759
        derive_more::Deref,
760
        derive_more::DerefMut,
761
        derive_more::AsRef,
762
        derive_more::AsMut,
763
    )]
764
    #[allow(clippy::exhaustive_structs)]
765
    pub struct ItemPresent<T: Default>(pub T);
766

            
767
    impl ItemSetMethods for P2MultiplicitySelector<NotPresent> {
768
        type Each = NotPresentEachValue;
769
        type Field = NotPresent;
770
        fn can_accumulate(self, _acc: &Option<NotPresent>) -> Result<(), EP> {
771
            Ok(())
772
        }
773
6
        fn accumulate(self, _: &mut Option<NotPresent>, _: NotPresentEachValue) -> Result<(), EP> {
774
6
            Ok(())
775
6
        }
776
726464
        fn finish(self, _acc: Option<NotPresent>, _: &'static str) -> Result<NotPresent, EP> {
777
726464
            Ok(NotPresent)
778
726464
        }
779
        fn debug_core(self) -> &'static str {
780
            "Ignored"
781
        }
782
    }
783

            
784
    impl ItemValueEncodable for NotPresentEachValue {
785
        fn write_item_value_onto(&self, _out: ItemEncoder) -> Result<(), Bug> {
786
            Err(internal!("NotPresentEachValue as ItemValueEncodable"))
787
        }
788
    }
789

            
790
    impl ArgumentSetMethods for P2MultiplicitySelector<NotPresent> {
791
        type Each = NotPresentEachValue;
792
        type Field = NotPresent;
793

            
794
106506
        fn parse_with<P>(self, _: &mut ArgumentStream<'_>, _: P) -> Result<Self::Field, AE>
795
106506
        where
796
106506
            P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result<Self::Each, AE>,
797
        {
798
106506
            Ok(NotPresent)
799
106506
        }
800

            
801
        fn debug_core(self) -> &'static str {
802
            "NotPresent"
803
        }
804
    }
805
    impl ItemArgument for NotPresentEachValue {
806
        fn write_arg_onto(&self, _out: &mut ItemEncoder) -> Result<(), Bug> {
807
            Err(internal!("NotPresentEachValue as ItemArgument"))
808
        }
809
    }
810
    impl ItemArgumentParseable for NotPresentEachValue {
811
        fn from_args<'s>(_: &mut ArgumentStream<'s>) -> Result<Self, ArgumentError> {
812
            // Not quite the right error, but we don't have an ArgumentError::Internal
813
            Err(AE::Unexpected)
814
        }
815
    }
816

            
817
    impl ItemObjectEncodable for NotPresentEachValue {
818
        fn label(&self) -> &str {
819
            "INTERNAL ERROR"
820
        }
821
        fn write_object_onto(&self, _b: &mut Vec<u8>) -> Result<(), Bug> {
822
            Err(internal!("NotPresentEachValue as ItemObjectEncodable"))
823
        }
824
    }
825

            
826
    impl ObjectSetMethods for P2MultiplicitySelector<NotPresent> {
827
        type Field = NotPresent;
828
        type Each = NotPresentEachValue;
829
3
        fn resolve_option(self, _found: Option<NotPresentEachValue>) -> Result<NotPresent, EP> {
830
3
            Ok(NotPresent)
831
3
        }
832
        fn debug_core(self) -> &'static str {
833
            "NotPresent"
834
        }
835
    }
836
    impl ItemObjectParseable for NotPresentEachValue {
837
1
        fn check_label(_label: &str) -> Result<(), EP> {
838
1
            Err(EP::ObjectUnexpected)
839
1
        }
840
        fn from_bytes(_input: &[u8]) -> Result<Self, EP> {
841
            Err(EP::ObjectUnexpected)
842
        }
843
    }
844

            
845
    impl<'f> encode::MultiplicityMethods<'f> for EMultiplicitySelector<NotPresent> {
846
        type Field = NotPresent;
847
        type Each = NotPresentEachValue;
848
97
        fn iter_ordered(self, _: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
849
97
            iter::empty()
850
97
        }
851
    }
852

            
853
    impl encode::OptionalityMethods for EMultiplicitySelector<NotPresent> {
854
        type Field = NotPresent;
855
        type Each = NotPresentEachValue;
856
1
        fn as_option<'f>(self, _: &'f Self::Field) -> Option<&'f Self::Each> {
857
1
            None
858
1
        }
859
    }
860

            
861
    impl FromStr for Ignored {
862
        type Err = Void;
863
        fn from_str(_s: &str) -> Result<Ignored, Void> {
864
            Ok(Ignored)
865
        }
866
    }
867

            
868
    impl ItemArgumentParseable for Ignored {
869
        fn from_args(_: &mut ArgumentStream) -> Result<Ignored, ArgumentError> {
870
            Ok(Ignored)
871
        }
872
    }
873

            
874
    impl ItemObjectParseable for Ignored {
875
3
        fn check_label(_label: &str) -> Result<(), EP> {
876
            // allow any label
877
3
            Ok(())
878
3
        }
879
3
        fn from_bytes(_input: &[u8]) -> Result<Self, EP> {
880
3
            Ok(Ignored)
881
3
        }
882
    }
883

            
884
    impl ObjectSetMethods for P2MultiplicitySelector<Ignored> {
885
        type Field = Ignored;
886
        type Each = Ignored;
887
3
        fn resolve_option(self, _found: Option<Ignored>) -> Result<Ignored, EP> {
888
3
            Ok(Ignored)
889
3
        }
890
        fn debug_core(self) -> &'static str {
891
            "Ignored"
892
        }
893
    }
894

            
895
    impl<'f> encode::MultiplicityMethods<'f> for EMultiplicitySelector<Ignored> {
896
        type Field = Ignored;
897
        type Each = IgnoredItemOrObjectValue;
898
        fn iter_ordered(self, _: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
899
            iter::empty()
900
        }
901
    }
902

            
903
    impl encode::OptionalityMethods for EMultiplicitySelector<Ignored> {
904
        type Field = Ignored;
905
        type Each = IgnoredItemOrObjectValue;
906
1
        fn as_option<'f>(self, _: &'f Self::Field) -> Option<&'f Self::Each> {
907
1
            None
908
1
        }
909
    }
910

            
911
    impl ItemValueEncodable for IgnoredItemOrObjectValue {
912
        fn write_item_value_onto(&self, _: ItemEncoder) -> Result<(), Bug> {
913
            void::unreachable(self.0)
914
        }
915
    }
916

            
917
    impl ItemObjectEncodable for IgnoredItemOrObjectValue {
918
        fn label(&self) -> &str {
919
            void::unreachable(self.0)
920
        }
921
        fn write_object_onto(&self, _: &mut Vec<u8>) -> Result<(), Bug> {
922
            void::unreachable(self.0)
923
        }
924
    }
925

            
926
    impl ItemArgumentParseable for NoMoreArguments {
927
58
        fn from_args(args: &mut ArgumentStream) -> Result<NoMoreArguments, ArgumentError> {
928
58
            Ok(args.reject_extra_args()?)
929
58
        }
930
    }
931

            
932
    impl ItemArgument for NoMoreArguments {
933
2
        fn write_arg_onto(&self, _: &mut ItemEncoder) -> Result<(), Bug> {
934
2
            Ok(())
935
2
        }
936
    }
937

            
938
    impl<T: Default> ItemValueParseable for ItemPresent<T> {
939
32802
        fn from_unparsed(item: UnparsedItem<'_>) -> StdResult<Self, EP> {
940
32802
            item.check_no_object()?;
941
32801
            Ok(Self::default())
942
32802
        }
943
    }
944

            
945
    impl<T: Default> ItemValueEncodable for ItemPresent<T> {
946
84
        fn write_item_value_onto(&self, out: ItemEncoder) -> StdResult<(), Bug> {
947
84
            out.finish();
948
84
            Ok(())
949
84
        }
950
    }
951
}
952

            
953
// ============================================================
954

            
955
/// Information about unknown values, which may have been retained as a `T`
956
///
957
/// Won't grow additional variants - but, `Retained` is only included conditionally.
958
///
959
/// Also used in the form `Unknown<()>` to indicate whether unknown values *should* be retained.
960
///
961
/// ### Example
962
///
963
/// ```
964
/// # {
965
/// #![cfg(feature = "retain-unknown")]
966
///
967
/// use tor_netdoc::types::Unknown;
968
///
969
/// let mut unk: Unknown<Vec<String>> = Unknown::new_retained_default();
970
/// unk.with_mut_unknown(|u| u.push("something-we-found".into()));
971
/// assert_eq!(unk.into_retained().unwrap(), ["something-we-found"]);
972
/// # }
973
/// ```
974
///
975
/// ### Equality comparison, semantics
976
///
977
/// Two `Unknown` are consider equal if both have the same record of unknown values,
978
/// or if neither records unknown values at all.
979
///
980
/// `Unknown` is not `Eq` or `Ord` because we won't want to relate a `Discarded`
981
/// to a `Retained`.  That would be a logic error.  `partial_cmp` gives `None` for this.
982
#[derive(Debug, PartialEq, Clone, Copy, Hash)]
983
#[allow(clippy::exhaustive_enums)] // this isn't going to change
984
pub enum Unknown<T> {
985
    /// The parsing discarded unknown values and they are no longer available.
986
    Discarded(PhantomData<T>),
987

            
988
    /// The document parsing retained (or should retain) unknown values.
989
    #[cfg(feature = "retain-unknown")]
990
    Retained(T),
991
}
992

            
993
impl<T> Unknown<T> {
994
    /// Create an `Unknown` which specifies that values were discarded (or should be)
995
959679
    pub fn new_discard() -> Self {
996
959679
        Unknown::Discarded(PhantomData)
997
959679
    }
998

            
999
    /// Map the `Retained`, if there is one
476137
    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Unknown<U> {
476137
        self.try_map(move |t| Ok::<_, Void>(f(t))).void_unwrap()
476137
    }
    /// Map the `Retained`, fallibly
476137
    pub fn try_map<U, E>(self, f: impl FnOnce(T) -> Result<U, E>) -> Result<Unknown<U>, E> {
476137
        Ok(match self {
475087
            Unknown::Discarded(_) => Unknown::Discarded(PhantomData),
            #[cfg(feature = "retain-unknown")]
1050
            Unknown::Retained(t) => Unknown::Retained(f(t)?),
        })
476137
    }
    /// Obtain an `Unknown` containing (maybe) a reference
2981
    pub fn as_ref(&self) -> Unknown<&T> {
2981
        match self {
            Unknown::Discarded(_) => Unknown::Discarded(PhantomData),
            #[cfg(feature = "retain-unknown")]
2981
            Unknown::Retained(t) => Unknown::Retained(t),
        }
2981
    }
    /// Return the retained unknown data, giving `None` if none was saved
    ///
    /// This is the function for disregarding the possible previously existence
    /// of now-discarded unknown (unrecognised) information.
    ///
    /// Use [`into_retained`](Self::into_retained) if it would be a bug
    /// if unrecognised information had been previously discarded.
42
    pub fn only_known(self) -> Option<T> {
42
        match self {
            Unknown::Discarded(_) => None,
            #[cfg(feature = "retain-unknown")]
42
            Unknown::Retained(t) => Some(t),
        }
42
    }
    /// Obtain the `Retained` data
    ///
    /// Treats lack of retention as an internal error.
2939
    pub fn into_retained(self) -> Result<T, Bug> {
2939
        match self {
            Unknown::Discarded(_) => Err(internal!("Unknown::retained but data not collected")),
            #[cfg(feature = "retain-unknown")]
2939
            Unknown::Retained(t) => Ok(t),
        }
2939
    }
    /// Start recording unknown information, with a default value for `T`
    #[cfg(feature = "retain-unknown")]
    pub fn new_retained_default() -> Self
    where
        T: Default,
    {
        Unknown::Retained(T::default())
    }
    /// Update the `Retained`, if there is one
    ///
    /// Intended for use in parsing, when we encounter an unknown value.
    ///
    /// Not provided in `try_` form.  If you think you need this, instead, unconditionally
    /// parse and verify the unknown value, and then conditionally insert it with this function.
    /// Don't parse it conditionally - that would skip some validation.
31655
    pub fn with_mut_unknown(&mut self, f: impl FnOnce(&mut T)) {
31655
        match self {
31538
            Unknown::Discarded(_) => {}
            #[cfg(feature = "retain-unknown")]
117
            Unknown::Retained(t) => f(t),
        }
31655
    }
}
impl<T: PartialOrd> PartialOrd for Unknown<T> {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        use Unknown::*;
        match (self, other) {
            (Discarded(_), Discarded(_)) => Some(cmp::Ordering::Equal),
            #[cfg(feature = "retain-unknown")]
            (Discarded(_), Retained(_)) | (Retained(_), Discarded(_)) => None,
            #[cfg(feature = "retain-unknown")]
            (Retained(a), Retained(b)) => a.partial_cmp(b),
        }
    }
}
// ============================================================
/// A finite floating point number
///
/// Suitable for `stats` items in voites' routerstatus entries:
/// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:stats>
///
/// Invariants:
///
///  * Is finite.  (So not NaN or Inf.)  Might be denormal.
///
/// String representation:
///
///  * Parses any valid C-like notation.
///
///  * Never uses exponential notation to display.
///
///  * Output can be rather large, up to 326 characters!
///    This is a spec bug.  The spec forbids us from using exponential notation.
///    <https://gitlab.torproject.org/tpo/core/torspec/-/work_items/416>
///
/// We may to change this in the future to use exponentials notation for output.
/// See <https://gitlab.torproject.org/tpo/core/torspec/-/work_items/416>
///
/// Comparison and ordering:
///
/// Implements `Eq` and `Ord` according to *mathematical* equality and ordering.
/// In particular, `-0.0` compares equal to `0.0`.
/// Therefore, does not implement `Hash`.
//
// TODO torspec#416 Consider replacing our F64Finite with finite f64 newtype from some crate
//
// What a palaver!
//
// This type is here rather than in rs.rs, in case similar things appears in other documents.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] //
#[derive(derive_more::Deref, derive_more::Into, derive_more::Display)]
pub struct F64Finite(f64);
/// Error converting an [`F64Finite`] from an `f64`: the value wasn't finite
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error, amplify::Getters)]
#[error("FP value {} ({bits:#x}) is not finite", f64::from_bits(self.bits))]
pub struct F64FiniteError {
    /// The raw bits (as from [`f64::to_bits`])
    //
    // We store it this way rather than as `f64` so that `Eq` etc. make sense.
    bits: u64,
}
impl TryFrom<f64> for F64Finite {
    type Error = F64FiniteError;
925
    fn try_from(v: f64) -> Result<Self, F64FiniteError> {
925
        v.is_finite()
925
            .then_some(F64Finite(v))
925
            .ok_or_else(|| F64FiniteError { bits: v.to_bits() })
925
    }
}
/// Error parsing [`F64Finite`] from a string
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
#[non_exhaustive]
pub enum F64FiniteParseError {
    /// Syntax error
    #[error("syntax error")]
    Syntax(#[from] std::num::ParseFloatError),
    /// Value is not finite
    #[error("bad value")]
    NotFinite(#[from] F64FiniteError),
}
impl FromStr for F64Finite {
    type Err = F64FiniteParseError;
921
    fn from_str(s: &str) -> StdResult<Self, F64FiniteParseError> {
921
        Ok(s.parse::<f64>()?.try_into()?)
921
    }
}
impl Eq for F64Finite {}
#[allow(clippy::derive_ord_xor_partial_ord)]
impl Ord for F64Finite {
    fn cmp(&self, other: &F64Finite) -> cmp::Ordering {
        self.0
            .partial_cmp(&other.0)
            .expect("finite f64 partial_cmp gave None")
    }
}
impl NormalItemArgument for F64Finite {}
// ============================================================
/// Known keyword (enum) value, or arbitrary string
///
/// `T` should be a `Copy` enum with unit variants.
/// It should have appropriate `FromStr` and `Display`,
/// as well as [`NormalItemArgument`], impls.
///
/// Then `KeywordOrString` will implement the same traits.
///
/// Unlike [`Unknown`], unknown values are always retained as strings.
//
// `RelayFlags` has machinery for parsing flags and retaining unknown values,
// but it uses `Unknown` to maybe discard unknown flags,
// and it is generally quite a lot more complicated.
#[derive(Debug, PartialEq, Clone, Hash)]
#[allow(clippy::exhaustive_enums)] // this isn't going to change
pub enum KeywordOrString<T: Copy> {
    /// Known and recognised `T`
    Known(T),
    /// Unknown value in arbitrary syntax
    Unknown(String),
}
impl<T: Copy + NormalItemArgument> NormalItemArgument for KeywordOrString<T> {}
impl<T: Copy + Display> Display for KeywordOrString<T> {
13
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
13
        match self {
13
            KeywordOrString::Known(t) => Display::fmt(t, f),
            KeywordOrString::Unknown(s) => Display::fmt(s, f),
        }
13
    }
}
impl<T: Copy + FromStr> FromStr for KeywordOrString<T> {
    type Err = Void;
61990
    fn from_str(s: &str) -> Result<Self, Void> {
61990
        Ok(match s.parse() {
61990
            Ok(y) => KeywordOrString::Known(y),
            Err(_) => KeywordOrString::Unknown(s.to_owned()),
        })
61990
    }
}
// ============================================================
/// A sequence of `T` items, with their order retained
///
/// Normally when a `Vec<T>` appears in a network document,
/// we expect the items to be sortable - they must impl [`EncodeOrd`](encode::EncodeOrd).
/// When encoding, the output is always sorted.
///
/// *This* type retains the ordering.
///
/// Implements the [`encode`] and [`parse2`] item multiplicity traits.
#[derive(Debug, Clone, Hash, Deftly, Eq, PartialEq, Ord, PartialOrd, Educe)]
#[educe(Default)]
#[derive_deftly(Transparent)]
#[allow(clippy::exhaustive_structs)]
pub struct RetainedOrderVec<T>(pub Vec<T>);
// ============================================================
/// Types for decoding times and dates
mod timeimpl {
    use super::*;
    use crate::{Error, NetdocErrorKind as EK, Pos, Result};
    use std::time::SystemTime;
    use time::{
        OffsetDateTime, PrimitiveDateTime, format_description::FormatItem,
        macros::format_description,
    };
    /// A wall-clock time, encoded in Iso8601 format with an intervening
    /// space between the date and time.
    ///
    /// (Example: "2020-10-09 17:38:12")
    #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Deftly)]
    #[derive_deftly(Transparent)]
    #[allow(clippy::exhaustive_structs)]
    pub struct Iso8601TimeSp(pub SystemTime);
    /// Formatting object for parsing the space-separated Iso8601 format.
    const ISO_8601SP_FMT: &[FormatItem] =
        format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
    impl FromStr for Iso8601TimeSp {
        type Err = Error;
123801
        fn from_str(s: &str) -> Result<Iso8601TimeSp> {
123801
            let d = PrimitiveDateTime::parse(s, &ISO_8601SP_FMT).map_err(|e| {
4
                EK::BadArgument
4
                    .at_pos(Pos::at(s))
4
                    .with_msg(format!("invalid time: {}", e))
4
            })?;
123797
            Ok(Iso8601TimeSp(d.assume_utc().into()))
123801
        }
    }
    /// Formats a SystemTime according to the given format description
    ///
    /// Also converts any time::error::format to fmt::Error
    /// so that it can be unwrapped in the Display trait impl
71
    fn fmt_with(
71
        t: SystemTime,
71
        format_desc: &[FormatItem],
71
    ) -> core::result::Result<String, fmt::Error> {
71
        OffsetDateTime::from(t)
71
            .format(format_desc)
71
            .map_err(|_| fmt::Error)
71
    }
    impl Display for Iso8601TimeSp {
68
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68
            write!(f, "{}", fmt_with(self.0, ISO_8601SP_FMT)?)
68
        }
    }
    /// A wall-clock time, encoded in ISO8601 format without an intervening
    /// space.
    ///
    /// This represents a specific UTC instant (ie an instant in global civil time).
    /// But it may not be able to represent leap seconds.
    ///
    /// The timezone is not included in the string representation; `+0000` is implicit.
    ///
    /// # Example
    ///
    /// ```
    /// use tor_netdoc::types::Iso8601TimeNoSp;
    ///
    /// let s = "2020-10-09T17:38:12";
    /// let t: Iso8601TimeNoSp = s.parse().unwrap();
    /// assert_eq!(t.to_string(), s);
    /// ```
    #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Deftly)]
    #[derive_deftly(Transparent)]
    #[allow(clippy::exhaustive_structs)]
    pub struct Iso8601TimeNoSp(pub SystemTime);
    /// Formatting object for parsing the space-separated Iso8601 format.
    const ISO_8601NOSP_FMT: &[FormatItem] =
        format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]");
    impl FromStr for Iso8601TimeNoSp {
        type Err = Error;
12
        fn from_str(s: &str) -> Result<Iso8601TimeNoSp> {
12
            let d = PrimitiveDateTime::parse(s, &ISO_8601NOSP_FMT).map_err(|e| {
4
                EK::BadArgument
4
                    .at_pos(Pos::at(s))
4
                    .with_msg(format!("invalid time: {}", e))
4
            })?;
8
            Ok(Iso8601TimeNoSp(d.assume_utc().into()))
12
        }
    }
    impl Display for Iso8601TimeNoSp {
3
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3
            write!(f, "{}", fmt_with(self.0, ISO_8601NOSP_FMT)?)
3
        }
    }
    impl crate::NormalItemArgument for Iso8601TimeNoSp {}
}
/// Types for decoding RSA keys
mod rsa {
    use super::*;
    use crate::{NetdocErrorKind as EK, Pos, Result};
    use std::ops::RangeBounds;
    use tor_llcrypto::pk::rsa::PublicKey;
    use tor_llcrypto::{d::Sha1, pk::rsa::KeyPair};
    /// The fixed exponent which we require when parsing any RSA key in a netdoc
    //
    // TODO this value is duplicated a lot in the v1 parser
    pub(crate) const RSA_FIXED_EXPONENT: u32 = 65537;
    /// The fixed exponent which we require when parsing any RSA key in a netdoc
    //
    // TODO this value is duplicated a lot in the v1 parser
    pub(crate) const RSA_MIN_BITS: usize = 1024;
    /// RSA public key, partially processed by `crate::paarse`.
    ///
    /// As parsed from a base64-encoded object.
    /// They key's properties (exponent and size) haven't been checked.
    #[allow(non_camel_case_types)]
    #[derive(Clone, Debug)]
    pub(crate) struct RsaPublicParse1Helper(PublicKey, Pos);
    /// RSA signature using SHA-1 as per "Signing documents" in dir-spec
    ///
    /// <https://spec.torproject.org/dir-spec/netdoc.html#signing>
    ///
    /// Used for
    /// [`AuthCert::dir-key-certification`](crate::doc::authcert::AuthCert::dir-key-certification),
    /// for example.
    ///
    /// # Caveats
    ///
    /// This type MUST NOT be used for anomalous signatures
    /// such as
    /// [`AuthCert::dir_key_crosscert`](crate::doc::authcert::AuthCert::dir_key_crosscert);
    /// in that case because `dir_key_crosscert`'s
    /// set of allowed object labels includes `ID SIGNATURE` whereas this type
    /// is always `SIGNATURE`
    #[derive(Debug, Clone, PartialEq, Eq, Deftly)]
    #[derive_deftly(ItemValueParseable, ItemValueEncodable)]
    #[deftly(netdoc(no_extra_args, signature(hash_accu = Sha1WholeKeywordLine)))]
    #[non_exhaustive]
    pub struct RsaSha1Signature {
        /// The bytes of the signature (base64-decoded).
        #[deftly(netdoc(object(label = "SIGNATURE"), with = crate::types::raw_data_object))]
        pub signature: Vec<u8>,
    }
    impl From<RsaPublicParse1Helper> for PublicKey {
3015
        fn from(k: RsaPublicParse1Helper) -> PublicKey {
3015
            k.0
3015
        }
    }
    impl super::FromBytes for RsaPublicParse1Helper {
3016
        fn from_bytes(b: &[u8], pos: Pos) -> Result<Self> {
3016
            let key = PublicKey::from_der(b)
3016
                .ok_or_else(|| EK::BadObjectVal.with_msg("unable to decode RSA public key"))?;
3015
            Ok(RsaPublicParse1Helper(key, pos))
3016
        }
    }
    impl RsaPublicParse1Helper {
        /// Give an error if the exponent of this key is not 'e'
3016
        pub(crate) fn check_exponent(self, e: u32) -> Result<Self> {
3016
            if self.0.exponent_is(e) {
3015
                Ok(self)
            } else {
1
                Err(EK::BadObjectVal
1
                    .at_pos(self.1)
1
                    .with_msg("invalid RSA exponent"))
            }
3016
        }
        /// Give an error if the length of this key's modulus, in
        /// bits, is not contained in 'bounds'
3018
        pub(crate) fn check_len<B: RangeBounds<usize>>(self, bounds: B) -> Result<Self> {
3018
            if bounds.contains(&self.0.bits()) {
3016
                Ok(self)
            } else {
2
                Err(EK::BadObjectVal
2
                    .at_pos(self.1)
2
                    .with_msg("invalid RSA length"))
            }
3018
        }
        /// Give an error if the length of this key's modulus, in
        /// bits, is not exactly `n`.
2671
        pub(crate) fn check_len_eq(self, n: usize) -> Result<Self> {
2671
            self.check_len(n..=n)
2671
        }
    }
    impl RsaSha1Signature {
        /// Make a signature according to "Signing documents" in the netdoc spec
        ///
        /// <https://spec.torproject.org/dir-spec/netdoc.html#signing>
        ///
        /// `NetdocEncoder` should have had the body of the document
        /// (everything except the signatures) already encoded.
        ///
        /// `item_keyword` is the keyword for the signature item.
        /// This is needed because different documents use different keywords,
        /// and the keyword is covered by the signature (an annoying is a layering violation).
        /// See <https://gitlab.torproject.org/tpo/core/torspec/-/issues/322>.
        ///
        /// # Example
        ///
        /// ```
        /// use derive_deftly::Deftly;
        /// use tor_error::Bug;
        /// use tor_llcrypto::pk::rsa;
        /// use tor_netdoc::derive_deftly_template_NetdocEncodable;
        /// use tor_netdoc::encode::{NetdocEncodable, NetdocEncoder};
        /// use tor_netdoc::types::RsaSha1Signature;
        ///
        /// #[derive(Deftly, Default)]
        /// #[derive_deftly(NetdocEncodable)]
        /// pub struct Document {
        ///     pub document_intro_keyword: (),
        /// }
        /// #[derive(Deftly)]
        /// #[derive_deftly(NetdocEncodable)]
        /// pub struct DocumentSignatures {
        ///     pub document_signature: RsaSha1Signature,
        /// }
        /// impl Document {
        ///     pub fn encode_sign(&self, k: &rsa::KeyPair) -> Result<String, Bug> {
        ///         let mut encoder = NetdocEncoder::new();
        ///         self.encode_unsigned(&mut encoder)?;
        ///         let document_signature =
        ///             RsaSha1Signature::new_sign_netdoc(k, &encoder, "document-signature")?;
        ///         let sigs = DocumentSignatures { document_signature };
        ///         sigs.encode_unsigned(&mut encoder)?;
        ///         let encoded = encoder.finish()?;
        ///         Ok(encoded)
        ///     }
        /// }
        ///
        /// # fn main() -> Result<(), anyhow::Error> {
        /// let k = rsa::KeyPair::generate(&mut tor_basic_utils::test_rng::testing_rng())?;
        /// let doc = Document::default();
        /// let encoded = doc.encode_sign(&k)?;
        /// assert!(encoded.starts_with(concat!(
        ///     "document-intro-keyword\n",
        ///     "document-signature\n",
        ///     "-----BEGIN SIGNATURE-----\n",
        /// )));
        /// # Ok(())
        /// # }
        /// ```
11
        pub fn new_sign_netdoc(
11
            private_key: &KeyPair,
11
            encoder: &NetdocEncoder,
11
            item_keyword: &str,
11
        ) -> StdResult<Self, Bug> {
11
            let mut h = Sha1::new();
11
            h.update(encoder.text_sofar()?);
11
            h.update(item_keyword);
11
            h.update("\n");
11
            let h = h.finalize();
11
            let signature = private_key
11
                .sign(&h)
11
                .map_err(into_internal!("RSA signing failed"))?;
11
            Ok(RsaSha1Signature { signature })
11
        }
    }
}
/// Types for decoding Ed25519 certificates
mod edcert {
    use std::result::Result as StdResult;
    use std::time::SystemTime;
    use crate::types::EmbeddedCert;
    use crate::{
        NetdocErrorKind as EK, Pos, Result,
        parse2::{ErrorProblem, VerifyFailed},
        types::EmbeddableCertObject,
    };
    use tor_cert::{CertType, CertifiedKey, Ed25519Cert, KeyUnknownCert};
    use tor_checkable::signed::SignatureGated;
    use tor_checkable::timed::TimeRangeBound;
    use tor_checkable::{SelfSigned, TimeBound};
    use tor_error::{Bug, into_internal};
    use tor_llcrypto::pk::ed25519::{self, Ed25519PublicKey, ValidatableEd25519Signature};
    /// An ed25519 certificate as parsed from a directory object, with
    /// signature not validated.
    #[derive(Debug, Clone)]
    pub(crate) struct UnvalidatedEdCert(KeyUnknownCert, Pos);
    impl super::FromBytes for UnvalidatedEdCert {
9436
        fn from_bytes(b: &[u8], p: Pos) -> Result<Self> {
9436
            let cert = Ed25519Cert::decode(b).map_err(|e| {
1
                EK::BadObjectVal
1
                    .at_pos(p)
1
                    .with_msg("Bad certificate")
1
                    .with_source(e)
1
            })?;
9435
            Ok(Self(cert, p))
9436
        }
9436
        fn from_vec(v: Vec<u8>, p: Pos) -> Result<Self> {
9436
            Self::from_bytes(&v[..], p)
9436
        }
    }
    impl UnvalidatedEdCert {
        /// Give an error if this certificate's type is not `desired_type`.
7203
        pub(crate) fn check_cert_type(self, desired_type: CertType) -> Result<Self> {
7203
            if self.0.peek_cert_type() != desired_type {
1
                return Err(EK::BadObjectVal.at_pos(self.1).with_msg(format!(
1
                    "bad certificate type {} (wanted {})",
1
                    self.0.peek_cert_type(),
1
                    desired_type
1
                )));
7202
            }
7202
            Ok(self)
7203
        }
        /// Give an error if this certificate's subject_key is not `pk`
4
        pub(crate) fn check_subject_key_is(self, pk: &ed25519::Ed25519Identity) -> Result<Self> {
4
            if self.0.peek_subject_key().as_ed25519() != Some(pk) {
1
                return Err(EK::BadObjectVal
1
                    .at_pos(self.1)
1
                    .with_msg("incorrect subject key"));
3
            }
3
            Ok(self)
4
        }
        /// Consume this object and return the inner Ed25519 certificate.
9434
        pub(crate) fn into_unchecked(self) -> KeyUnknownCert {
9434
            self.0
9434
        }
    }
    /// An Ed25519 identity certificate.
    ///
    /// This is a certificate of [`CertType::IDENTITY_V_SIGNING`] where the
    /// relay's long-term ed25519 identity key signs the relay's medium-term
    /// ed25519 signing key, used for signing almost all other certifications
    /// associated with a given relay.
    #[derive(Debug, Clone, PartialEq, Eq)]
    #[allow(clippy::exhaustive_structs)]
    pub struct Ed25519IdentityCert {
        /// The long-term ed25519 identity key of the relay
        pub id_ed25519: ed25519::Ed25519Identity,
        /// The medium-term ed25519 signing key of the relay.
        pub sign_ed25519: ed25519::Ed25519Identity,
    }
    impl EmbeddableCertObject<KeyUnknownCert> for Ed25519IdentityCert {
        const LABEL: &str = "ED25519 CERT";
    }
    impl Ed25519IdentityCert {
        /// Verifies the validity of an [`Ed25519IdentityCert`].
        ///
        /// # Requirements
        ///
        /// 1. MUST have the identity key in the `signed-with-ed25519-key` extension.
        /// 2. MUST have a valid signature by the identity key.
        /// 3. MUST be of [`CertType::IDENTITY_V_SIGNING`].
        /// 4. Certified key MUST BE of [`tor_cert::CertifiedKey::Ed25519`].
        /// 5. Both keys MUST be valid mappings to a [`ed25519::PublicKey`].
12511
        pub fn verify(cert: KeyUnknownCert) -> StdResult<TimeRangeBound<Self>, VerifyFailed> {
12511
            let cert = cert
                // 1. MUST have the identity key in the `signed-with-ed25519-key` extension.
12511
                .should_have_signing_key()
12511
                .map_err(|_| VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData))?
                // 2. MUST have a valid signature by the identity key.
12510
                .check_signature()?
                // Okay to call because we create TimeRangeBound later.
                // TODO DIRAUTH: Use TimeRangeBound instead.
12510
                .dangerously_assume_timely();
            // 3. MUST be of [`CertType::IDENTITY_V_SIGNING`].
12510
            if cert.cert_type() != CertType::IDENTITY_V_SIGNING {
1
                return Err(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData));
12509
            }
            // Bug is alright because .should_have_signing_key() assured us.
12509
            let id_ed25519 = *cert.signing_key().ok_or(VerifyFailed::Bug)?;
            // 4. Certified key MUST BE of [`tor_cert::CertifiedKey::Ed25519`].
12509
            let sign_ed25519 = *cert
12509
                .subject_key()
12509
                .as_ed25519()
12509
                .ok_or(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData))?;
            // 5. Both keys MUST be valid mappings to a [`ed25519::PublicKey`].
            // Unsure if this check is required or implied by (2) but defensive
            // programming does not hurt.
12508
            if ed25519::PublicKey::try_from(id_ed25519).is_err()
12508
                || ed25519::PublicKey::try_from(sign_ed25519).is_err()
            {
                return Err(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData));
12508
            }
12508
            Ok(TimeRangeBound::new(
12508
                Self {
12508
                    id_ed25519,
12508
                    sign_ed25519,
12508
                },
12508
                ..cert.expiry(),
12508
            ))
12511
        }
        /// Creates a new signed [`Ed25519IdentityCert`].
3
        pub fn new_signed(
3
            id_ed25519: &ed25519::Keypair,
3
            sign_ed25519: ed25519::Ed25519Identity,
3
            expiry: SystemTime,
3
        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
3
            let cert = Ed25519Cert::builder()
3
                .expiration(expiry)
3
                .signing_key(id_ed25519.public_key().into())
3
                .cert_type(CertType::IDENTITY_V_SIGNING)
3
                .cert_key(sign_ed25519.into())
3
                .encode_and_sign(id_ed25519)
3
                .map_err(into_internal!("failed to encode and sign identity cert"))?;
3
            let cert =
3
                Ed25519Cert::decode(&cert).map_err(into_internal!("decode just encoded cert"))?;
3
            Ok(EmbeddedCert::new(
3
                Self {
3
                    id_ed25519: id_ed25519.public_key().into(),
3
                    sign_ed25519,
3
                },
3
                cert,
3
            ))
3
        }
    }
    /// An Ed25519 family certificate.
    ///
    /// This is a certificate of [`CertType::FAMILY_V_IDENTITY`] where the
    /// family key signs the long-term ed25519 identity key of the given relay.
    ///
    /// It purposely does not store the long-term ed25519 identity key of the
    /// relay because the idea of this type should be equal only to other types
    /// with the same family key.
    #[derive(Debug, Clone, PartialEq, Eq)]
    #[allow(clippy::exhaustive_structs)]
    pub struct Ed25519FamilyCert {
        /// The public key of the family.
        // TODO: We probably want to add a getter for this returning the
        // family name as in:
        // <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:family-cert>
        pub family_ed25519: ed25519::Ed25519Identity,
    }
    impl EmbeddableCertObject<KeyUnknownCert> for Ed25519FamilyCert {
        const LABEL: &str = "FAMILY CERT";
    }
    impl Ed25519FamilyCert {
        /// Verifies the validity of an [`Ed25519FamilyCert`].
        ///
        /// For such a certificate to be valid, the caller must provide a
        /// known Ed25519 identity key of the relay beforehand.
        ///
        /// # Requirements
        ///
        /// 1. MUST have the `signed-with-ed25519-key` extension containing the family key.
        /// 2. MUST have a valid signature by the family key.
        /// 3. MUST be of of [`CertType::FAMILY_V_IDENTITY`].
        /// 4. Certified key MUST BE of [`tor_cert::CertifiedKey::Ed25519`].
        /// 5. `id_ed25519` MUST be the certified key.
        /// 6. Both keys MUST be valid mappings to a [`ed25519::PublicKey`].
483
        pub fn verify(
483
            id_ed25519: ed25519::Ed25519Identity,
483
            cert: KeyUnknownCert,
483
        ) -> StdResult<TimeRangeBound<Self>, VerifyFailed> {
483
            let cert = cert
                // 1. MUST have the `signed-with-ed25519-key` extension containing the family key.
483
                .should_have_signing_key()?
                // 2. MUST have a valid signature by the family key.
482
                .check_signature()?
                // Okay to call because we create TimeRangeBound later.
                // TODO DIRAUTH: Use TimeRangeBound instead.
482
                .dangerously_assume_timely();
            // 3. MUST be of of [`CertType::FAMILY_V_IDENTITY`].
482
            if cert.cert_type() != CertType::FAMILY_V_IDENTITY {
1
                return Err(ErrorProblem::ObjectInvalidData.into());
481
            }
            // Bug is alright because .should_have_signing_key() assured us.
481
            let family_ed25519 = *cert.signing_key().ok_or(VerifyFailed::Bug)?;
            // 4. Certified key MUST BE of [`tor_cert::CertifiedKey::Ed25519`].
481
            let certified_key = *cert
481
                .subject_key()
481
                .as_ed25519()
481
                .ok_or(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData))?;
            // 5. `id_ed25519` MUST be the certified key.
480
            if certified_key != id_ed25519 {
                return Err(VerifyFailed::VerifyFailed);
480
            }
            // 6. Both keys MUST be valid mappings to a [`ed25519::PublicKey`].
480
            if ed25519::PublicKey::try_from(family_ed25519).is_err()
480
                || ed25519::PublicKey::try_from(id_ed25519).is_err()
            {
                return Err(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData));
480
            }
480
            Ok(TimeRangeBound::new(
480
                Self { family_ed25519 },
480
                ..cert.expiry(),
480
            ))
483
        }
        /// Creates a new signed [`Ed25519FamilyCert`].
1
        pub fn new_signed(
1
            family_ed25519: &ed25519::Keypair,
1
            id_ed25519: ed25519::Ed25519Identity,
1
            expiry: SystemTime,
1
        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
1
            let cert = Ed25519Cert::builder()
1
                .expiration(expiry)
1
                .signing_key(family_ed25519.public_key().into())
1
                .cert_type(CertType::FAMILY_V_IDENTITY)
1
                .cert_key(id_ed25519.into())
1
                .encode_and_sign(family_ed25519)
1
                .map_err(into_internal!("failed to encode and sign family cert"))?;
1
            let cert =
1
                Ed25519Cert::decode(&cert).map_err(into_internal!("decode just encoded cert"))?;
1
            Ok(EmbeddedCert::new(
1
                Self {
1
                    family_ed25519: family_ed25519.public_key().into(),
1
                },
1
                cert,
1
            ))
1
        }
    }
    /// Verified reverse cert by K_ntor on KP_relayid_ed
    ///
    /// This certificate is signed by KS_ntor
    /// (the circuit extension key) and certifies
    /// KP_relayid_ed25519 ed25519 identity key of the relay.
    ///
    /// The type itself is zero-sized because it provides no new useful
    /// information that cannot be found elsewhere within the router descriptor.
    /// It is intended for use within
    /// [`EmbeddedCert`]`<Ed25519NtorCrossCert, KeyUnknownCert>`
    ///
    /// # Note on key conversion
    ///
    /// Keep in mind however that the ntor onion key is only provided as an
    /// X25519 key and *not* an Ed25519 key, meaning that interfacing
    /// applications have to convert it using a function such as
    /// [`tor_llcrypto::pk::keymanip::convert_curve25519_to_ed25519_public()`].
    /// This also requires obtaining the sign bit which is usually given as an
    /// argument in the `ntor-onion-key-crosscert` item.  However, this is
    /// outside of the scope of this struct and the code will assume that
    /// callers have already converted the X25519 public key to an Ed25519
    /// public key as outlined in the specifications.
    ///
    /// # See Also
    ///
    /// * <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:ntor-onion-key-crosscert>
    /// * <https://spec.torproject.org/dir-spec/converting-to-ed25519.html>
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    #[non_exhaustive]
    pub struct Ed25519NtorCrossCert {
        /// Explicit field, to avoid constructing this accidentally without
        /// doing all the verification.
        _promise_we_verified: (),
    }
    impl EmbeddableCertObject<KeyUnknownCert> for Ed25519NtorCrossCert {
        const LABEL: &str = "ED25519 CERT";
    }
    impl Ed25519NtorCrossCert {
        /// Verifies the validity of an [`Ed25519NtorCrossCert`].
        ///
        /// For such a certificate to be valid, the caller must provide a known
        /// Ed25519 identity key and Ed25519 ntor onion key of the relay
        /// beforehand.
        ///
        /// # Requirements
        ///
        /// 1. MUST be of [`CertType::NTOR_CC_IDENTITY`].
        /// 2. Certified key MUST be of [`CertifiedKey::Ed25519`].
        /// 3. Certified key MUST be equal to `id_ed25519`.
        /// 4. MUST have a valid signature.
12510
        pub fn verify(
12510
            ntor_ed25519: ed25519::Ed25519Identity,
12510
            id_ed25519: ed25519::Ed25519Identity,
12510
            cert: KeyUnknownCert,
12510
        ) -> StdResult<TimeRangeBound<Self>, VerifyFailed> {
            Ok(
                // .verify_inner() ensures 1-3.
12510
                Self::verify_inner(ntor_ed25519, id_ed25519, cert)?
                    .0
                    // 4. MUST have a valid signature.
12508
                    .check_signature()?,
            )
12510
        }
        /// Creates a new signed [`Ed25519NtorCrossCert`].
2
        pub fn new_signed(
2
            ntor_ed25519: &ed25519::ExpandedKeypair,
2
            id_ed25519: ed25519::Ed25519Identity,
2
            expiry: SystemTime,
2
        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
2
            let cert = Ed25519Cert::builder()
2
                .expiration(expiry)
2
                .cert_type(CertType::NTOR_CC_IDENTITY)
2
                .cert_key(id_ed25519.into())
2
                .encode_and_sign(ntor_ed25519)
2
                .map_err(into_internal!("failed to encode and sign ntor cert"))?;
2
            let cert =
2
                Ed25519Cert::decode(&cert).map_err(into_internal!("decode just encoded cert"))?;
2
            Ok(EmbeddedCert::new(
2
                Self {
2
                    _promise_we_verified: (),
2
                },
2
                cert,
2
            ))
2
        }
        /// Verifies the validity of a [`KeyUnknownCert`] believed to be a
        /// [`CertType::NTOR_CC_IDENTITY`].
        ///
        /// This function serves as glue between the legacy parser and
        /// [`Self::verify()`].
        ///
        /// # Requirements
        ///
        /// 1. MUST be of [`CertType::NTOR_CC_IDENTITY`].
        /// 2. Certified key MUST be of [`CertifiedKey::Ed25519`].
        /// 3. Certified key MUST be equal to `id_ed25519`.
        ///
        /// # Return Type
        ///
        /// Actual signature and time validation is done by the caller, hence
        /// why it returns a gated type as the first element of the tuple.
        /// The other elements constitute the inner signature plus the
        /// SystemTime denoting the expiry.  This is required for integration
        /// with legacy parser in order to enable pushing it to the verification
        /// batch, as the [`tor_checkable`] primitives do not provide access
        /// to the inner signatures/expiries and also do not support operations
        /// like cloning due to being dyn.
14743
        pub(crate) fn verify_inner(
14743
            ntor_ed25519: ed25519::Ed25519Identity,
14743
            id_ed25519: ed25519::Ed25519Identity,
14743
            cert: KeyUnknownCert,
14743
        ) -> StdResult<
14743
            (
14743
                SignatureGated<TimeRangeBound<Self>>,
14743
                ValidatableEd25519Signature,
14743
                SystemTime,
14743
            ),
14743
            VerifyFailed,
14743
        > {
            // 1. MUST be of [`CertType::NTOR_CC_IDENTITY`].
14743
            if cert.peek_cert_type() != CertType::NTOR_CC_IDENTITY {
1
                return Err(ErrorProblem::ObjectInvalidData.into());
14742
            }
            // 2. Certified key MUST be of [`CertifiedKey::Ed25519`].
            // 3. Certified key MUST be equal to `id_ed25519`.
14742
            if cert.peek_subject_key() != &CertifiedKey::Ed25519(id_ed25519) {
1
                return Err(VerifyFailed::VerifyFailed);
14741
            }
            // Fish out the signature from the certificate and verify it later.
            //
            // It may fail if ntor_ed25519 is not a valid mapping to a public
            // key.  This is okay.  The .should_be_signed_with() call is
            // tor_cert boilerplate and only required to obtain an
            // UncheckedCert, as ntor cross-certificates do not contain the
            // signed-with extension.
14741
            let (cert, sig) = cert
14741
                .should_be_signed_with(&ntor_ed25519)?
14741
                .dangerously_split()?;
            // Fish out the expiration date from the certificate.
            //
            // Important: We must not set SystemTime::UNIX_EPOCH as the lower
            // bound, because with TimeRangeBound, a lower-bound of zero is not
            // equal to an absent lower bound!
14741
            let cert = cert.dangerously_assume_timely();
14741
            let expiration = ..cert.expiry();
14741
            Ok((
14741
                SignatureGated::new(
14741
                    TimeRangeBound::new(
14741
                        Self {
14741
                            _promise_we_verified: (),
14741
                        },
14741
                        expiration,
14741
                    ),
14741
                    vec![Box::new(sig.clone())],
14741
                ),
14741
                sig,
14741
                expiration.end,
14741
            ))
14743
        }
        /// Internal function for creating an unverified instance.
        ///
        /// This is only intended for testing and legacy parser compatibility
        /// purposes.
2234
        pub(crate) fn dangerous_new_unverified() -> Self {
2234
            Self {
2234
                _promise_we_verified: (),
2234
            }
2234
        }
    }
}
/// Digest identifiers, and digests in the form `ALGORITHM=BASE64U`
///
/// As found in a vote's `m` line.
// TODO Use FixedB64 here.
mod identified_digest {
    use super::*;
    define_derive_deftly! {
        /// impl `FromStr` and `Display` for an enum with unit variants but also "unknown"
        ///
        /// Expected input: an enum whose variants are either
        ///  * unit variants, perhaps with `#[deftly(string_repr = "string")]`
        ///  * singleton tuple variant, containing `String` (or near equivalent)
        ///
        /// If `#[deftly(string_repro)]` is not specified,
        /// the default is snake case of the variant name.
        //
        // This macro may seem overkill, but open-coding these impls gives opportunities
        // for mismatches between FromStr, Display, and the variant name.
        //
        // TODO consider putting this in tor-basic-utils (maybe with a better name),
        // or possibly asking if derive_more want their FromStr to have this.
        StringReprUnitsOrUnknown for enum, expect items, beta_deftly:
        ${define STRING_REPR {
            ${vmeta(string_repr)
              as str,
              default { ${concat ${snake_case $vname}} }
            }
        }}
        ${impl FromStr} {
            type Err = Void;
469
            fn from_str(s: &str) -> Result<Self, Void> {
                $(
                    ${when v_is_unit}
                    if s == $STRING_REPR {
                        return Ok($vtype)
                    }
                )
                $(
                    ${when not(v_is_unit)} // anything else had better be Unknown
                    // not using `return ..;` makes this a syntax error if there are several.
                    Ok($vtype { 0: s.into() })
                )
            }
        }
        ${impl AsRef<str>} {
15
            fn as_ref(&self) -> &str {
                match self {
                    $(
                        ${when v_is_unit}
                        $vtype => $STRING_REPR,
                    )
                    $(
                        ${when not(v_is_unit)}
                        $vpat => f_0,
                    )
                }
            }
        }
        ${impl Display} {
15
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                let s: &str = self.as_ref();
                Display::fmt(s, f)
            }
        }
    }
    /// The name of a digest algorithm.
    ///
    /// Can represent an unrecognised algorithm, so it's parsed and reproduced.
    #[derive(Debug, Clone, Eq, PartialEq, Hash, Deftly)]
    #[derive_deftly(StringReprUnitsOrUnknown)]
    #[non_exhaustive]
    pub enum DigestName {
        /// SHA-256
        Sha256,
        /// Unknown
        Unknown(String),
    }
    /// A single digest made with a nominated digest algorithm, `ALGORITHM=DIGEST`
    #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, derive_more::Display)]
    #[display("{alg}={value}")]
    #[non_exhaustive]
    pub struct IdentifiedDigest {
        /// The algorithm name.
        alg: DigestName,
        /// The digest value.
        ///
        /// Invariant: length is correct for `alg`, assuming `alg` is known.
        value: B64,
    }
    impl NormalItemArgument for DigestName {}
    impl NormalItemArgument for IdentifiedDigest {}
    /// Invalid syntax parsing an `IdentifiedDigest`
    #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, thiserror::Error)]
    #[error("invalid syntax, expected ALGORITHM=DIGEST: {0}")]
    pub struct IdentifiedDigestParseError(String);
    impl Ord for DigestName {
        fn cmp(&self, other: &Self) -> Ordering {
            self.as_ref().cmp(other.as_ref())
        }
    }
    impl PartialOrd for DigestName {
        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
            Some(self.cmp(other))
        }
    }
    impl FromStr for IdentifiedDigest {
        type Err = IdentifiedDigestParseError;
469
        fn from_str(s: &str) -> Result<Self, Self::Err> {
469
            (|| {
469
                let (alg, value) = s.split_once('=').ok_or("missing equals sign")?;
469
                let alg = alg.parse().void_unwrap();
469
                let value = value
469
                    .parse::<B64>()
469
                    .map_err(|e| format!("bad value: {}", e.report()))?;
469
                if let Some(exp_len) = (|| {
                    Some({
                        use DigestName::*;
469
                        match alg {
469
                            Sha256 => 32,
                            Unknown(_) => None?,
                        }
                    })
                })() {
469
                    let val_len = value.as_bytes().len();
469
                    if val_len != exp_len {
                        return Err(format!("got {val_len} bytes, expected {exp_len}"));
469
                    }
                }
469
                Ok(IdentifiedDigest { alg, value })
            })()
469
            .map_err(IdentifiedDigestParseError)
469
        }
    }
}
/// Types for decoding RSA fingerprints
mod fingerprint {
    use super::*;
    use crate::parse2::{ArgumentError, ArgumentStream, ItemArgumentParseable};
    use crate::{Error, NetdocErrorKind as EK, Pos, Result};
    use base64ct::{Base64Unpadded, Encoding as _};
    use itertools::Itertools;
    use tor_llcrypto::pk::rsa::RsaIdentity;
    /// A hex-encoded RSA key identity (fingerprint) with spaces in it.
    ///
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html?highlight=fingerprint#item:fingerprint>
    ///
    /// Netdoc parsing adapter for [`RsaIdentity`]
    #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
    #[derive_deftly(Transparent)]
    #[allow(clippy::exhaustive_structs)]
    pub struct SpFingerprint(pub RsaIdentity);
    /// A hex-encoded fingerprint with no spaces.
    ///
    /// Netdoc parsing adapter for [`RsaIdentity`]
    #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
    #[derive_deftly(Transparent)]
    #[allow(clippy::exhaustive_structs)]
    pub struct Fingerprint(pub RsaIdentity);
    /// A base64-encoded fingerprint (unpadded)
    ///
    /// Netdoc parsing adapter for [`RsaIdentity`]
    #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
    #[derive_deftly(Transparent)]
    #[allow(clippy::exhaustive_structs)]
    pub struct Base64Fingerprint(pub RsaIdentity);
    /// A "long identity" in the format used for Family members.
    ///
    /// Netdoc parsing adapter for [`RsaIdentity`]
    #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
    #[derive_deftly(Transparent)]
    #[allow(clippy::exhaustive_structs)]
    pub(crate) struct LongIdent(pub RsaIdentity);
    /// Helper: parse an identity from a hexadecimal string
1194834
    fn parse_hex_ident(s: &str) -> Result<RsaIdentity> {
1194834
        RsaIdentity::from_hex(s).ok_or_else(|| {
2133
            EK::BadArgument
2133
                .at_pos(Pos::at(s))
2133
                .with_msg("wrong length on fingerprint")
2133
        })
1194834
    }
    impl FromStr for SpFingerprint {
        type Err = Error;
2238
        fn from_str(s: &str) -> Result<SpFingerprint> {
2238
            let ident = parse_hex_ident(&s.replace(' ', "")).map_err(|e| e.at_pos(Pos::at(s)))?;
2236
            Ok(SpFingerprint(ident))
2238
        }
    }
    impl ItemArgumentParseable for SpFingerprint {
12587
        fn from_args<'s>(
12587
            args: &mut ArgumentStream<'s>,
12587
        ) -> std::result::Result<Self, ArgumentError> {
            // Take the first 10 arguments because an SpFingerprint consists of
            // 10 x 4 = 40 characters.
12587
            let fp = args.take(10).collect::<Vec<_>>();
            // Less than 10 means missing arguments.
12587
            if fp.len() < 10 {
1
                return Err(ArgumentError::Missing);
12586
            }
            // More than 10 should be impossible due to .take(10).
12586
            debug_assert_eq!(fp.len(), 10);
            // All arguments must be 4 characters long.
125852
            if fp.iter().any(|arg| arg.len() != 4) {
1
                return Err(ArgumentError::Invalid);
12585
            }
            // Convert it to a string without spaces, RsaIdentity::from_hex will
            // verify the rest.
            Ok(Self(
12585
                RsaIdentity::from_hex(fp.join("").as_str()).ok_or(ArgumentError::Invalid)?,
            ))
12587
        }
    }
    impl encode::ItemArgument for SpFingerprint {
31
        fn write_arg_onto(&self, out: &mut ItemEncoder<'_>) -> StdResult<(), Bug> {
31
            let res = self
31
                .0
31
                .to_bytes()
31
                .chunks(2)
310
                .map(|b| format!("{:02X}{:02X}", b[0], b[1]))
31
                .join(" ");
31
            debug_assert_eq!(res.len(), 4 * 10 + 9);
31
            out.args_raw_string(&res);
31
            Ok(())
31
        }
    }
    impl FromStr for Base64Fingerprint {
        type Err = Error;
223681
        fn from_str(s: &str) -> Result<Base64Fingerprint> {
223681
            let b = s.parse::<super::B64>()?;
223681
            let ident = RsaIdentity::from_bytes(b.as_bytes()).ok_or_else(|| {
                EK::BadArgument
                    .at_pos(Pos::at(s))
                    .with_msg("Wrong identity length")
            })?;
223681
            Ok(Base64Fingerprint(ident))
223681
        }
    }
    impl Display for Base64Fingerprint {
302
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
302
            Display::fmt(&Base64Unpadded::encode_string(self.as_bytes()), f)
302
        }
    }
    impl FromStr for Fingerprint {
        type Err = Error;
389187
        fn from_str(s: &str) -> Result<Fingerprint> {
389187
            let ident = parse_hex_ident(s).map_err(|e| e.at_pos(Pos::at(s)))?;
389182
            Ok(Fingerprint(ident))
389187
        }
    }
    impl Display for Fingerprint {
101
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
101
            Display::fmt(&hex::encode_upper(self.as_bytes()), f)
101
        }
    }
    impl FromStr for LongIdent {
        type Err = Error;
803409
        fn from_str(mut s: &str) -> Result<LongIdent> {
803409
            s = s.strip_prefix('$').unwrap_or(s);
            // Strip at '=' or '~' if found.
803409
            s = s.split_once(['=', '~']).map(|(a, _)| a).unwrap_or(s);
803409
            let ident = parse_hex_ident(s)?;
801283
            Ok(LongIdent(ident))
803409
        }
    }
    impl Display for LongIdent {
1272
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1272
            write!(f, "${}", self.0.as_hex_upper())
1272
        }
    }
    impl crate::NormalItemArgument for Fingerprint {}
    impl crate::NormalItemArgument for Base64Fingerprint {}
    impl crate::NormalItemArgument for LongIdent {}
}
/// A type for relay nicknames
mod nickname {
    use super::*;
    use tinystr::TinyAsciiStr;
    /// This is a strange limit, but it comes from Tor.
    const MAX_NICKNAME_LEN: usize = 19;
    /// The nickname for a Tor relay.
    ///
    /// These nicknames are legacy mechanism that's occasionally useful in
    /// debugging. They should *never* be used to uniquely identify relays;
    /// nothing prevents two relays from having the same nickname.
    ///
    /// Nicknames are required to be ASCII, alphanumeric, and between 1 and 19
    /// characters inclusive.
    #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
    pub struct Nickname(tinystr::TinyAsciiStr<MAX_NICKNAME_LEN>);
    /// Invalid nickname
    #[derive(Clone, Debug, thiserror::Error)]
    #[error("invalid nickname")]
    #[non_exhaustive]
    pub struct InvalidNickname {}
    impl Nickname {
        /// Return a view of this nickname as a string slice.
363
        pub fn as_str(&self) -> &str {
363
            self.0.as_str()
363
        }
    }
    impl Display for Nickname {
95
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95
            self.as_str().fmt(f)
95
        }
    }
    impl FromStr for Nickname {
        type Err = InvalidNickname;
818608
        fn from_str(s: &str) -> Result<Self, InvalidNickname> {
818608
            let tiny = TinyAsciiStr::from_str(s).map_err(|_| InvalidNickname {})?;
818606
            if tiny.is_ascii_alphanumeric() && !tiny.is_empty() {
818604
                Ok(Nickname(tiny))
            } else {
2
                Err(InvalidNickname {})
            }
818608
        }
    }
    impl AsRef<str> for Nickname {
265
        fn as_ref(&self) -> &str {
265
            self.as_str()
265
        }
    }
    impl crate::NormalItemArgument for Nickname {}
}
/// Hostnames etc.
//
// TODO maybe move all this to tor-basic-utils
mod hostname {
    use super::*;
    use std::net::IpAddr;
    /// Internet hostname
    ///
    /// Valid according to Internet RFC1123,
    /// with the additional restriction that there must be at least one letter.
    /// (That means that anything accepted as a `Hostname`
    /// won't be accepted as an address even by very relaxed IPv4 address parsers.
    /// We presume that no TLD will ever exist that is entirely decimal digits.)
    ///
    /// Preserves case.
    ///
    /// Reserved hostname such as `example.come`, `tor.invalid` and `localhost`
    /// are accepted.
    ///
    /// # Comparisons; `PartialEq`, `Eq`
    ///
    /// The `PartialEq` and `Eq` implementations are case sensitive,
    /// even though internet hostnames are not case-sensitive.
    ///
    /// Comparing hostnames for identical apparent meaning is complicated.
    /// If you need to do that, you (may) need to engage with punycode (IDN),
    /// as well as arranging for a case-insensitive comparison.
    ///
    /// And of course, hostnames reference to the DNS.
    /// A single host may have multiple names and it may change its address.
    #[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] //
    #[derive(derive_more::Into, derive_more::Deref, derive_more::AsRef, derive_more::Display)]
    pub struct Hostname(String);
    /// Hostname, or IP address (v4 or v6)
    ///
    /// Preserves hostname case.  See [`Hostname`].
    ///
    /// Reserved hostnames and addresses (eg `0.0.0.0` or `tor.invalid`) are accepted.
    ///
    /// IPv6 addresses are represented *without* surrounding `[ ]`.
    ///
    /// Therefore, you cannot make this into a host-and-port by appending `:port`.
    /// To process name-and-port is complex.  `SRV` (or `MX`) records might be involved.
    //
    // This type is called `InternetHost` to emphasise that it is primarily for
    // hosts on the public internet and, unlike arti-client's `Host`,
    // has special handling of `.onion` addresses.
    #[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] //
    #[derive(derive_more::Display)]
    #[allow(clippy::exhaustive_enums)]
    // TODO derive .as_hostname(), .as_ip_addr(), From<Hostname>, From<IpAddr>
    pub enum InternetHost {
        /// Hostname
        #[display("{_0}")]
        Name(Hostname),
        /// IP address (v4 or v6)
        #[display("{_0}")]
        IpAddr(IpAddr),
    }
    /// Invalid hostname
    #[derive(Clone, Debug, thiserror::Error)]
    #[error("invalid hostname")]
    #[non_exhaustive]
    pub struct InvalidHostname {}
    /// Invalid Internet hostname/address
    #[derive(Clone, Debug, thiserror::Error)]
    #[error("invalid: not a valid hostname, nor a valid IPv4 or IPv6 address")]
    #[non_exhaustive]
    pub struct InvalidInternetHost {}
    impl Hostname {
        /// Obtain this hostname as a `str`
4
        pub fn as_str(&self) -> &str {
4
            &self.0
4
        }
    }
    impl AsRef<str> for Hostname {
        fn as_ref(&self) -> &str {
            self.as_str()
        }
    }
    impl TryFrom<String> for Hostname {
        type Error = InvalidHostname;
146
        fn try_from(s: String) -> Result<Self, InvalidHostname> {
146
            if hostname_validator::is_valid(&s) &&
                // Reject hostnames that consist only of decimal digits and full stops.
                // Some of those are accepted by some old IPv4 address parsers.
                // If any fool makes a TLD that is only digits, they deserve everything they get.
166
                !s.chars().all(|c| c.is_ascii_digit() || c == '.')
            {
124
                Ok(Hostname(s))
            } else {
22
                Err(InvalidHostname {})
            }
146
        }
    }
    impl FromStr for Hostname {
        type Err = InvalidHostname;
146
        fn from_str(s: &str) -> Result<Self, InvalidHostname> {
146
            s.to_owned().try_into()
146
        }
    }
    impl FromStr for InternetHost {
        type Err = InvalidInternetHost;
127883
        fn from_str(s: &str) -> Result<Self, InvalidInternetHost> {
127883
            if let Ok(y) = s.parse() {
127754
                Ok(InternetHost::IpAddr(y))
129
            } else if let Ok(y) = s.parse() {
120
                Ok(InternetHost::Name(y))
            } else {
                // For simplicity, we  discard the errors from parsing the options
                // rather than trying to reproduce them.  Why something isn't a valid
                // address or hostname ought to be fairly obvious.
9
                Err(InvalidInternetHost {})
            }
127883
        }
    }
    impl NormalItemArgument for Hostname {}
    impl NormalItemArgument for InternetHost {}
}
/// Contact information of the relay operator.
mod contact_info {
    use super::*;
    /// `contact` item: contact information (eg of a relay dirauth operator)
    ///
    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:contact>
    ///
    /// Also used for authority entries in netstatus documents.
    #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Deftly)] //
    #[derive(derive_more::Into, derive_more::AsRef, derive_more::Deref, derive_more::Display)]
    #[derive_deftly(ItemValueEncodable)]
    #[non_exhaustive]
    pub struct ContactInfo(#[deftly(netdoc(rest))] String);
    /// Contact information (`contact` item value) has invalid syntax
    #[derive(Clone, Debug, thiserror::Error)]
    #[error("contact information (`contact` item value) has invalid syntax")]
    #[non_exhaustive]
    pub struct InvalidContactInfo {}
    impl FromStr for ContactInfo {
        type Err = InvalidContactInfo;
137516
        fn from_str(s: &str) -> Result<Self, InvalidContactInfo> {
            // TODO torspec#396 we should probably impose more restrictions
            // For now we forbid `\n` and initial whitespace, which is enough to ensure
            // that all values will roundtrip unchanged through netdoc encoding and parsing.
137516
            if s.contains('\n') || s.starts_with(char::is_whitespace) {
2
                Err(InvalidContactInfo {})
            } else {
137514
                Ok(ContactInfo(s.to_owned()))
            }
137516
        }
    }
    impl ItemValueParseable for ContactInfo {
136373
        fn from_unparsed(mut item: UnparsedItem<'_>) -> Result<Self, parse2::ErrorProblem> {
136373
            item.check_no_object()?;
136373
            item.args_mut()
136373
                .into_remaining()
136373
                .parse()
136373
                .map_err(|_e| item.args().handle_error("info", ArgumentError::Invalid))
136373
        }
    }
}
/// Types for boolean-like types.
mod boolean {
    use derive_deftly::Deftly;
    use std::{
        fmt::Display,
        ops::{Deref, DerefMut},
        str::FromStr,
    };
    use crate::{Error, NetdocErrorKind as EK, NormalItemArgument, Pos};
    /// A boolean that is represented by a `0` (false) or `1` (true).
    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Deftly)]
    #[derive_deftly(Transparent)]
    #[allow(clippy::exhaustive_structs)]
    pub struct NumericBoolean(pub bool);
    impl FromStr for NumericBoolean {
        type Err = Error;
12606
        fn from_str(s: &str) -> Result<Self, Self::Err> {
12606
            match s {
12606
                "0" => Ok(Self(false)),
9670
                "1" => Ok(Self(true)),
1
                _ => Err(EK::BadArgument
1
                    .at_pos(Pos::at(s))
1
                    .with_msg("Invalid numeric boolean")),
            }
12606
        }
    }
    impl Display for NumericBoolean {
32
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32
            write!(f, "{}", u8::from(self.0))
32
        }
    }
    impl NormalItemArgument for NumericBoolean {}
}
#[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::{
        fmt::Debug,
        time::{Duration, SystemTime},
    };
    use itertools::Itertools;
    use base64ct::Encoding;
    use tor_basic_utils::test_rng::testing_rng;
    use tor_cert::{CertType, CertifiedKey, Ed25519Cert, KeyUnknownCert};
    use tor_checkable::{TimeBound, timed::TimeRangeBound};
    use tor_llcrypto::pk::ed25519::{self, Ed25519Identity, Ed25519PublicKey, ExpandedKeypair};
    use super::*;
    use crate::{
        Pos, Result,
        encode::{NetdocEncodable, encode_netdoc_unsigned},
        parse2::{ErrorProblem, ParseInput, VerifyFailed},
        types::EmbeddedCert,
    };
    /// Decode s as a multi-line base64 string, ignoring ascii whitespace.
    fn base64_decode_ignore_ws(s: &str) -> std::result::Result<Vec<u8>, base64ct::Error> {
        let mut s = s.to_string();
        s.retain(|c| !c.is_ascii_whitespace());
        base64ct::Base64::decode_vec(s.as_str())
    }
    #[test]
    fn base64() -> Result<()> {
        // Test parsing success:
        // Unpadded:
        assert_eq!("Mi43MTgyOA".parse::<B64>()?.as_bytes(), &b"2.71828"[..]);
        assert!("Mi43MTgyOA".parse::<B64>()?.check_len(7..8).is_ok());
        assert_eq!("Mg".parse::<B64>()?.as_bytes(), &b"2"[..]);
        assert!("Mg".parse::<B64>()?.check_len(1..2).is_ok());
        assert_eq!(
            "8J+NkvCfjZLwn42S8J+NkvCfjZLwn42S"
                .parse::<B64>()?
                .as_bytes(),
            "🍒🍒🍒🍒🍒🍒".as_bytes()
        );
        assert!(
            "8J+NkvCfjZLwn42S8J+NkvCfjZLwn42S"
                .parse::<B64>()?
                .check_len(24..25)
                .is_ok()
        );
        assert!(
            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz8="
                .parse::<B64>()?
                .check_len(32..33)
                .is_ok()
        );
        // Padded:
        assert_eq!("Mi43MTgyOA==".parse::<B64>()?.as_bytes(), &b"2.71828"[..]);
        assert!("Mi43MTgyOA==".parse::<B64>()?.check_len(7..8).is_ok());
        assert_eq!("Mg==".parse::<B64>()?.as_bytes(), &b"2"[..]);
        assert!("Mg==".parse::<B64>()?.check_len(1..2).is_ok());
        // Test parsing failures:
        // Invalid character.
        assert!("Mi43!!!!!!".parse::<B64>().is_err());
        // Invalid last character.
        assert!("Mi".parse::<B64>().is_err());
        assert!(
            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxaaaa"
                .parse::<B64>()
                .is_err()
        );
        // Invalid length.
        assert!("Mi43MTgyOA".parse::<B64>()?.check_len(8..).is_err());
        Ok(())
    }
    #[test]
    fn base64_lengths() -> Result<()> {
        assert_eq!("".parse::<B64>()?.as_bytes(), b"");
        assert!("=".parse::<B64>().is_err());
        assert!("==".parse::<B64>().is_err());
        assert!("B".parse::<B64>().is_err());
        assert!("B=".parse::<B64>().is_err());
        assert!("B==".parse::<B64>().is_err());
        assert!("Bg=".parse::<B64>().is_err());
        assert_eq!("Bg".parse::<B64>()?.as_bytes(), b"\x06");
        assert_eq!("Bg==".parse::<B64>()?.as_bytes(), b"\x06");
        assert_eq!("BCg".parse::<B64>()?.as_bytes(), b"\x04\x28");
        assert_eq!("BCg=".parse::<B64>()?.as_bytes(), b"\x04\x28");
        assert!("BCg==".parse::<B64>().is_err());
        assert_eq!("BCDE".parse::<B64>()?.as_bytes(), b"\x04\x20\xc4");
        assert!("BCDE=".parse::<B64>().is_err());
        assert!("BCDE==".parse::<B64>().is_err());
        Ok(())
    }
    #[test]
    fn base64_rev() {
        use base64ct::{Base64, Base64Unpadded};
        // Check that strings that we accept are precisely ones which
        // can be generated by either Base64 or Base64Unpadded
        for n in 0..=5 {
            for c_vec in std::iter::repeat_n("ACEQg/=".chars(), n).multi_cartesian_product() {
                let s: String = c_vec.into_iter().collect();
                #[allow(clippy::print_stderr)]
                let b = match s.parse::<B64>() {
                    Ok(b) => {
                        eprintln!("{:10} {:?}", s, b.as_bytes());
                        b
                    }
                    Err(_) => {
                        eprintln!("{:10} Err", s);
                        continue;
                    }
                };
                let b = b.as_bytes();
                let ep = Base64::encode_string(b);
                let eu = Base64Unpadded::encode_string(b);
                assert!(
                    s == ep || s == eu,
                    "{:?} decoded to {:?} giving neither {:?} nor {:?}",
                    s,
                    b,
                    ep,
                    eu
                );
            }
        }
    }
    #[test]
    fn base16() -> anyhow::Result<()> {
        let chk = |s: &str, b: &[u8]| -> anyhow::Result<()> {
            let parsed = s.parse::<B16>()?;
            assert_eq!(parsed.as_bytes(), b, "{s:?}");
            assert_eq!(parsed.to_string(), s.to_ascii_lowercase());
            let parsed = s.parse::<B16U>()?;
            assert_eq!(parsed.as_bytes(), b, "{s:?}");
            assert_eq!(parsed.to_string(), s.to_ascii_uppercase());
            Ok(())
        };
        chk("332e313432", b"3.142")?;
        chk("332E313432", b"3.142")?;
        chk("332E3134", b"3.14")?;
        assert!("332E313".parse::<B16>().is_err());
        assert!("332G3134".parse::<B16>().is_err());
        Ok(())
    }
    #[test]
    fn curve25519() -> Result<()> {
        use tor_llcrypto::pk::curve25519::PublicKey;
        let k1 = "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz8=";
        let k2 = hex::decode("a69c2d8475d6f245c3d1ff5f13b50f62c38002ee2e8f9391c12a2608cc4a933f")
            .unwrap();
        let k2: &[u8; 32] = &k2[..].try_into().unwrap();
        let k1: PublicKey = k1.parse::<Curve25519Public>()?.into();
        assert_eq!(k1, (*k2).into());
        assert!(
            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz"
                .parse::<Curve25519Public>()
                .is_err()
        );
        assert!(
            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORSomCMxKkz"
                .parse::<Curve25519Public>()
                .is_err()
        );
        assert!(
            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5wSomCMxKkz"
                .parse::<Curve25519Public>()
                .is_err()
        );
        assert!(
            "ppwthHXW8kXD0f9fE7UPYsOAAu4ORwSomCMxKkz"
                .parse::<Curve25519Public>()
                .is_err()
        );
        Ok(())
    }
    #[test]
    fn ed25519() -> Result<()> {
        use tor_llcrypto::pk::ed25519::Ed25519Identity;
        let k1 = "WVIPQ8oArAqLY4XzkcpIOI6U8KsUJHBQhG8SC57qru0";
        let k2 = hex::decode("59520f43ca00ac0a8b6385f391ca48388e94f0ab14247050846f120b9eeaaeed")
            .unwrap();
        let k1: Ed25519Identity = k1.parse::<Ed25519Public>()?.into();
        assert_eq!(k1, Ed25519Identity::from_bytes(&k2).unwrap());
        assert!(
            "WVIPQ8oArAqLY4Xzk0!!!!8KsUJHBQhG8SC57qru"
                .parse::<Ed25519Public>()
                .is_err()
        );
        assert!(
            "WVIPQ8oArAqLY4XzkcpIU8KsUJHBQhG8SC57qru"
                .parse::<Ed25519Public>()
                .is_err()
        );
        assert!(
            "WVIPQ8oArAqLY4XzkcpIU8KsUJHBQhG8SC57qr"
                .parse::<Ed25519Public>()
                .is_err()
        );
        // right length, bad key:
        assert!(
            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxaaaa"
                .parse::<Curve25519Public>()
                .is_err()
        );
        Ok(())
    }
    #[test]
    fn time() -> Result<()> {
        use humantime::parse_rfc3339;
        use std::time::SystemTime;
        let t = "2020-09-29 13:36:33".parse::<Iso8601TimeSp>()?;
        let t: SystemTime = t.into();
        assert_eq!(t, parse_rfc3339("2020-09-29T13:36:33Z").unwrap());
        assert!("2020-FF-29 13:36:33".parse::<Iso8601TimeSp>().is_err());
        assert!("2020-09-29Q13:99:33".parse::<Iso8601TimeSp>().is_err());
        assert!("2020-09-29".parse::<Iso8601TimeSp>().is_err());
        assert!("too bad, waluigi time".parse::<Iso8601TimeSp>().is_err());
        assert_eq!(
            "2020-09-29 13:36:33",
            "2020-09-29 13:36:33".parse::<Iso8601TimeSp>()?.to_string()
        );
        let t = "2020-09-29T13:36:33".parse::<Iso8601TimeNoSp>()?;
        let t: SystemTime = t.into();
        assert_eq!(t, parse_rfc3339("2020-09-29T13:36:33Z").unwrap());
        assert!("2020-09-29 13:36:33".parse::<Iso8601TimeNoSp>().is_err());
        assert!("2020-09-29Q13:99:33".parse::<Iso8601TimeNoSp>().is_err());
        assert!("2020-09-29".parse::<Iso8601TimeNoSp>().is_err());
        assert!("too bad, waluigi time".parse::<Iso8601TimeNoSp>().is_err());
        assert_eq!(
            "2020-09-29T13:36:33",
            "2020-09-29T13:36:33"
                .parse::<Iso8601TimeNoSp>()?
                .to_string()
        );
        Ok(())
    }
    #[test]
    fn rsa_public_key() {
        // Taken from a chutney network.
        let key_b64 = r#"
        MIIBigKCAYEAsDkzTcKS4kAF56R2ijb9qCek53tKC1EwMdpWMk58bB28fY6kHc55
        E7n1hB+LC5neZlx88GKuZ9k8P3g0MlO5ejalcfBdIIm28Nz86JXf/L23YnEpxnG/
        IpxZEcmx/EYN+vwp72W3DGuzyntaoaut6lGJk+O/aRCLLcTm4MNznvN1ackK2H6b
        Xm2ejRwtVRLoPKODJiPGl43snCfXXWsMH3IALFOgm0szPLv2fAJzBI8VWrUN81M/
        lgwJhG6+xbr1CkrXI5fKs/TNr0B0ydC9BIZplmPrnXaeNklnw1cqUJ1oxDSgBrvx
        rpDo7paObjSPV26opa68QKGa7Gu2MZQC3RzViNCbawka/108g6hSUkoM+Om2oivr
        DvtMOs10MjsfibEBVnwEhqnlb/gj3hJkYoGRsCwAyMIaMObHcmAevMJRWAjGCc8T
        GMS9dSmg1IZst+U+V2OCcIHXT6wZ1zPsBM0pYKVLCwtewaq1306k0n+ekriEo7eI
        FS3Dd/Dx/a6jAgMBAAE=
        "#;
        let key_bytes = base64_decode_ignore_ws(key_b64).unwrap();
        let rsa = RsaPublicParse1Helper::from_vec(key_bytes, Pos::None).unwrap();
        let bits = tor_llcrypto::pk::rsa::PublicKey::from(rsa.clone()).bits();
        assert_eq!(bits, 3072);
        // tests on a valid key
        assert!(rsa.clone().check_exponent(65537).is_ok());
        assert!(rsa.clone().check_exponent(1337).is_err());
        assert!(rsa.clone().check_len_eq(3072).is_ok());
        assert!(rsa.clone().check_len(1024..=4096).is_ok());
        assert!(rsa.clone().check_len(1024..=1024).is_err());
        assert!(rsa.check_len(4096..).is_err());
        // A string of bytes that is not an RSA key.
        let failure = RsaPublicParse1Helper::from_vec(vec![1, 2, 3], Pos::None);
        assert!(failure.is_err());
    }
    #[test]
    fn ed_cert() {
        use tor_llcrypto::pk::ed25519::Ed25519Identity;
        // From a chutney network.
        let cert_b64 = r#"
        AQQABwRNAR6m3kq5h8i3wwac+Ti293opoOP8RKGP9MT0WD4Bbz7YAQAgBACGCdys
        G7AwsoYMIKenDN6In6ReiGF8jaYoGqmWKDVBdGGMDIZyNIq+VdhgtAB1EyNFHJU1
        jGM0ir9dackL+PIsHbzJH8s/P/8RfUsKIL6/ZHbn3nKMxLH/8kjtxp5ScAA=
        "#;
        let cert_bytes = base64_decode_ignore_ws(cert_b64).unwrap();
        // From the cert above.
        let right_subject_key: Ed25519Identity = "HqbeSrmHyLfDBpz5OLb3eimg4/xEoY/0xPRYPgFvPtg"
            .parse::<Ed25519Public>()
            .unwrap()
            .into();
        // From `ed25519()` test above.
        let wrong_subject_key: Ed25519Identity = "WVIPQ8oArAqLY4XzkcpIOI6U8KsUJHBQhG8SC57qru0"
            .parse::<Ed25519Public>()
            .unwrap()
            .into();
        // decode and check correct type and key
        let cert = UnvalidatedEdCert::from_vec(cert_bytes, Pos::None)
            .unwrap()
            .check_cert_type(tor_cert::CertType::IDENTITY_V_SIGNING)
            .unwrap()
            .check_subject_key_is(&right_subject_key)
            .unwrap();
        // check wrong type.
        assert!(
            cert.clone()
                .check_cert_type(tor_cert::CertType::RSA_ID_X509)
                .is_err()
        );
        // check wrong key.
        assert!(cert.check_subject_key_is(&wrong_subject_key).is_err());
        // Try an invalid object that isn't a certificate.
        let failure = UnvalidatedEdCert::from_vec(vec![1, 2, 3], Pos::None);
        assert!(failure.is_err());
    }
    #[test]
    fn fingerprint() -> Result<()> {
        use tor_llcrypto::pk::rsa::RsaIdentity;
        let fp1 = "7467 A97D 19CD 2B4F 2BC0 388A A99C 5E67 710F 847E";
        let fp2 = "7467A97D19CD2B4F2BC0388AA99C5E67710F847E";
        let fp3 = "$7467A97D19CD2B4F2BC0388AA99C5E67710F847E";
        let fp4 = "$7467A97D19CD2B4F2BC0388AA99C5E67710F847E=fred";
        let k = hex::decode(fp2).unwrap();
        let k = RsaIdentity::from_bytes(&k[..]).unwrap();
        assert_eq!(RsaIdentity::from(fp1.parse::<SpFingerprint>()?), k);
        assert_eq!(RsaIdentity::from(fp2.parse::<SpFingerprint>()?), k);
        assert!(fp3.parse::<SpFingerprint>().is_err());
        assert!(fp4.parse::<SpFingerprint>().is_err());
        assert!(fp1.parse::<Fingerprint>().is_err());
        assert_eq!(RsaIdentity::from(fp2.parse::<Fingerprint>()?), k);
        assert!(fp3.parse::<Fingerprint>().is_err());
        assert!(fp4.parse::<Fingerprint>().is_err());
        assert_eq!(Fingerprint(k).to_string(), fp2);
        assert!(fp1.parse::<LongIdent>().is_err());
        assert_eq!(RsaIdentity::from(fp2.parse::<LongIdent>()?), k);
        assert_eq!(RsaIdentity::from(fp3.parse::<LongIdent>()?), k);
        assert_eq!(RsaIdentity::from(fp4.parse::<LongIdent>()?), k);
        assert!("xxxx".parse::<Fingerprint>().is_err());
        assert!("ffffffffff".parse::<Fingerprint>().is_err());
        let fp_b64 = "dGepfRnNK08rwDiKqZxeZ3EPhH4";
        assert_eq!(RsaIdentity::from(fp_b64.parse::<Base64Fingerprint>()?), k);
        assert_eq!(Base64Fingerprint(k).to_string(), fp_b64);
        Ok(())
    }
    #[test]
    fn nickname() -> anyhow::Result<()> {
        let n: Nickname = "Foo".parse()?;
        assert_eq!(n.as_str(), "Foo");
        assert_eq!(n.to_string(), "Foo");
        let word = "Untr1gonometr1cally";
        assert_eq!(word.len(), 19);
        let long: Nickname = word.parse()?;
        assert_eq!(long.as_str(), word);
        let too_long = "abcdefghijklmnopqrstuvwxyz";
        let not_ascii = "Eyjafjallajökull";
        let too_short = "";
        let other_invalid = "contains space";
        assert!(not_ascii.len() <= 19);
        assert!(too_long.parse::<Nickname>().is_err());
        assert!(not_ascii.parse::<Nickname>().is_err());
        assert!(too_short.parse::<Nickname>().is_err());
        assert!(other_invalid.parse::<Nickname>().is_err());
        Ok(())
    }
    /// Test for both `Hostname` and `InternetHost`
    #[test]
    fn hostname() {
        use std::net::IpAddr;
        // Test a string that we should treat as a valid hostname.
        let chk_name = |s: &str| {
            let n: Hostname = s.parse().expect(s);
            assert_eq!(n.as_str(), s);
            assert_eq!(n.to_string(), s);
            assert_eq!(s.parse::<InternetHost>().expect(s), InternetHost::Name(n));
        };
        // Test a string that looks like it could be an address or a hostname.
        // We parse those as addresses.
        let chk_either = |s: &str| {
            let h: InternetHost = s.parse().expect(s);
            let a: IpAddr = s.parse().expect(s);
            assert_eq!(h, InternetHost::IpAddr(a), "{s:?}");
            assert_eq!(h.to_string(), a.to_string(), "{s:?}");
        };
        // Test a string that's an address, and isn't a valid hostname.
        let chk_addr = |s: &str| {
            let _: InvalidHostname = s.parse::<Hostname>().expect_err(s);
            chk_either(s);
        };
        // Test a string that we should reject.
        let chk_bad = |s: &str| {
            let _: InvalidHostname = s.parse::<Hostname>().expect_err(s);
            let _: InvalidInternetHost = s.parse::<InternetHost>().expect_err(s);
        };
        chk_name("foo.bar");
        chk_name("localhost");
        chk_name("tor.invalid");
        chk_name("example.com");
        // Unarguably invalid.
        chk_bad("");
        chk_bad("foo bar");
        chk_bad("foo..bar");
        chk_bad("foo.-bar");
        chk_bad(" foo.bar ");
        chk_bad("[::1]");
        // Strings that some IP address parsers accept as addresses,
        // but which are also valid hostnames according to RFC1123.
        //
        // We reject them rather than processing of them as hostnames,
        // exposing downstream software to possible strangeness.
        chk_bad("1");
        chk_bad("127.0.0.023");
        // No-one thinks this is a valid IP address but we reject it as a hostname too,
        // even though it's syntactically legal per RFC1123, because it's quite bad.
        chk_bad("1.2.3.4.5");
        chk_either("0.0.0.0");
        chk_either("127.0.0.1");
        chk_addr("::");
        chk_addr("::1");
        chk_addr("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
        chk_addr("::ffff:192.0.2.3"); // IPv6-mapped IPv4 address
    }
    #[test]
    fn contact_info() -> anyhow::Result<()> {
        use parse2::{ParseInput, parse_netdoc};
        const S: &str = "some relay operator";
        let n: ContactInfo = S.parse()?;
        assert_eq!(n.as_str(), S);
        assert_eq!(n.to_string(), S);
        let bad = |s: &str| {
            let _: InvalidContactInfo = s.parse::<ContactInfo>().unwrap_err();
        };
        bad(" starts with space");
        bad("contains\nnewline");
        #[derive(PartialEq, Debug, Deftly)]
        #[derive_deftly(NetdocParseable, NetdocEncodable)]
        struct TestDoc {
            pub intro: (),
            pub contact: ContactInfo,
        }
        let roundtrip = |s: &str| -> anyhow::Result<()> {
            let doc = TestDoc {
                intro: (),
                contact: s.parse()?,
            };
            let enc = encode_netdoc_unsigned([&doc])?;
            let reparsed = parse_netdoc::<TestDoc>(&ParseInput::new(&enc, "<test>"))?;
            assert_eq!(doc, reparsed);
            Ok(())
        };
        roundtrip("normal")?;
        roundtrip("trailing  white space  ")?;
        roundtrip("wtf is this allowed in \x03 netdocs\r")?; // TODO torspec#396
        Ok(())
    }
    /// Round trip test for [`NumericBoolean`] ensuring that `0` is false,
    /// `1` is true, and other things fail.
    #[test]
    fn numeric_boolean() {
        let chk = |s: &str| {
            assert_eq!(NumericBoolean::from_str(s).expect(s).to_string(), s);
        };
        chk("0");
        chk("1");
        // Testing this because it is not a u8.
        assert!(NumericBoolean::from_str("10000").is_err());
    }
    #[test]
    fn f64_finite() {
        let normalise_string = |i: &str, o: &str| {
            let v: F64Finite = i.parse().expect(i);
            assert_eq!(v.to_string(), o, "i={i:?}");
        };
        let roundtrip_string = |s: &str| normalise_string(s, s);
        let roundtrip_value = |i: f64| {
            let v: F64Finite = i.try_into().unwrap();
            let s = v.to_string();
            let o: F64Finite = s.parse().expect(&s);
            assert_eq!(v, o, "{i:?} {s}");
            assert_eq!(v.to_bits(), o.to_bits(), "{i:?} {s}");
        };
        let error_string = |s: &str| {
            let _: F64FiniteParseError = s.parse::<F64Finite>().expect_err(s);
        };
        roundtrip_string("0");
        roundtrip_string("0.5");
        roundtrip_string("1");
        roundtrip_string("42");
        roundtrip_string("9007199254740991"); // f64::MAX_EXACT_INTEGER (as per Rust 1.96.0)
        normalise_string("1e3", "1000");
        roundtrip_value(f64::EPSILON);
        roundtrip_value(f64::EPSILON + 1.0);
        roundtrip_value(f64::MIN);
        roundtrip_value(f64::MIN_POSITIVE);
        roundtrip_value(-f64::MIN_POSITIVE);
        roundtrip_value(f64::MAX);
        error_string(&f64::NAN.to_string());
        error_string(&f64::INFINITY.to_string());
        error_string("");
        error_string("garbage");
        // TODO torspec#416 these ought to be more reasonable, but this is what it does now:
        roundtrip_string(
            "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014",
        ); // MIN_POSITIVE
        roundtrip_string(
            "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
        ); // MAX
    }
    /// Test that ensures SpFingerprint matches the 10x4 requirement.
    #[test]
    fn sp_fingerprint() {
        use derive_deftly::Deftly;
        use tor_llcrypto::pk::rsa::RsaIdentity;
        use crate::parse2::ErrorProblem;
        #[derive(Deftly)]
        #[derive_deftly(NetdocParseable, NetdocEncodable)]
        struct Wrapper {
            #[deftly(netdoc(single_arg))]
            fingerprint: SpFingerprint,
        }
        /// Small helper to parse an [`SpFingerprint`].
        ///
        /// In the case the parsing went successful, it also performs a
        /// round-trip encoding test.
        fn parse2(s: &str) -> std::result::Result<SpFingerprint, ErrorProblem> {
            use crate::parse2::{self, ParseInput};
            let input = format!("fingerprint {s}\n");
            let res = parse2::parse_netdoc::<Wrapper>(&ParseInput::new(&input, ""))
                .map_err(|x| x.problem)?;
            // Round-trip encoding; we only do .starts_with() because input
            // may contain trailing parameters which will obviously not be
            // encoded; trimming to remove the trailing "\n" afterwards.
            let mut enc = NetdocEncoder::default();
            res.encode_unsigned(&mut enc).unwrap();
            assert!(input.starts_with(enc.finish().unwrap().trim_end()));
            Ok(res.fingerprint)
        }
        // Test a valid one.
        assert_eq!(
            parse2(&vec!["ABAB"; 10].join(" ")).unwrap(),
            SpFingerprint(RsaIdentity::from_bytes(&[0xAB; 20]).unwrap())
        );
        // Test a valid one with tail.
        assert_eq!(
            parse2(&vec!["ABAB"; 11].join(" ")).unwrap(),
            SpFingerprint(RsaIdentity::from_bytes(&[0xAB; 20]).unwrap())
        );
        // Missing argument
        assert!(matches!(
            parse2(&vec!["ABAB"; 9].join(" ")).unwrap_err(),
            ErrorProblem::MissingArgument { .. }
        ));
        // Invalid argument.
        // In this case, we have string with a total length of 40 but with
        // one pair having 6 characters and another one having 2 as a proof
        // of that.
        assert!(matches!(
            parse2("0000 000000 00 0000 0000 0000 0000 0000 0000 0000").unwrap_err(),
            ErrorProblem::InvalidArgument { .. }
        ));
        // And of course invalid hex should fail too.
        assert!(matches!(
            parse2(&vec!["ZZZZ"; 10].join(" ")).unwrap_err(),
            ErrorProblem::InvalidArgument { .. }
        ));
    }
    /// Verifies the parsing of [`ItemPresent`].
    #[test]
    fn item_present_parse2() {
        #[derive(Default)]
        struct Token;
        #[derive(Deftly)]
        #[derive_deftly(NetdocParseable)]
        struct TestDoc {
            #[allow(unused)]
            intro: Ignored,
            foo: Option<ItemPresent<Token>>,
        }
        // The test cases with their respective result; boolean indicating that
        // it was present.
        let tests = [
            // Test valid present.
            ("intro\nfoo\n", Ok(true)),
            // Test valid absent.
            ("intro\n", Ok(false)),
            // Test repeated.
            ("intro\nfoo\nfoo\n", Err(ErrorProblem::ItemRepeated)),
            // Test repeated with unknown.
            ("intro\nbar\nfoo\nfoo\n", Err(ErrorProblem::ItemRepeated)),
            // Test with argument.
            ("intro\nfoo bar\n", Ok(true)),
            // Test with two arguments.
            ("intro\nfoo bar baz\n", Ok(true)),
            // Test with object.
            (
                "intro\nfoo\n-----BEGIN RSA PUBLIC KEY-----\n-----END RSA PUBLIC KEY-----\n",
                Err(ErrorProblem::ObjectUnexpected),
            ),
        ];
        for (input, expect) in tests {
            println!("{input:?}, {expect:?}");
            // Convert the result by calling .is_present() and extracting EP.
            let got = parse2::parse_netdoc::<TestDoc>(&ParseInput::new(input, ""))
                .map(|x| x.foo.is_some())
                .map_err(|e| e.problem);
            assert_eq!(got, expect);
        }
    }
    #[test]
    fn item_present_encode() {
        #[derive(Default)]
        struct Token;
        #[derive(Deftly)]
        #[derive_deftly(NetdocEncodable)]
        struct TestDoc {
            #[allow(unused)]
            intro: (),
            foo: Option<ItemPresent<Token>>,
        }
        let tests = [
            (Some(ItemPresent(Token)), "intro\nfoo\n"),
            (None, "intro\n"),
        ];
        for (present, output) in tests {
            let re_encoded = encode_netdoc_unsigned([&TestDoc {
                intro: (),
                foo: present,
            }])
            .unwrap();
            assert_eq!(re_encoded, output);
        }
    }
    /// Helper to call methods for edcerts.
    trait Ed25519CertTest: Sized + PartialEq + Eq + Debug {
        /// Creates a new instance.
        ///
        /// Used to create a struct in ad-hoc fashion for Eq comparison.
        fn new(
            signing_key: ed25519::Ed25519Identity,
            certified_key: ed25519::Ed25519Identity,
        ) -> Self;
        /// Returns the expected [`CertType`].
        fn cert_type() -> CertType;
        /// Calls .new_signed().
        ///
        /// This method is used to create an [`EmbeddedCert`] with a given
        /// signing key and a key that shall be certified.
        fn new_signed(
            signing_key: &ed25519::Keypair,
            certified_key: ed25519::Ed25519Identity,
            expiry: SystemTime,
        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug>;
        /// Calls .verify().
        ///
        /// The method verifies a certificate given a pre-known certified key,
        /// the actual certificate, and a timestamp.
        fn verify(
            signing_key: Option<ed25519::Ed25519Identity>,
            certified_key: ed25519::Ed25519Identity,
            cert: KeyUnknownCert,
        ) -> StdResult<TimeRangeBound<Self>, VerifyFailed>;
    }
    impl Ed25519CertTest for Ed25519IdentityCert {
        fn new(
            signing_key: ed25519::Ed25519Identity,
            certified_key: ed25519::Ed25519Identity,
        ) -> Self {
            Self {
                id_ed25519: signing_key,
                sign_ed25519: certified_key,
            }
        }
        fn cert_type() -> CertType {
            CertType::IDENTITY_V_SIGNING
        }
        fn new_signed(
            signing_key: &ed25519::Keypair,
            certified_key: ed25519::Ed25519Identity,
            expiry: SystemTime,
        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
            Self::new_signed(signing_key, certified_key, expiry)
        }
        fn verify(
            _signing_key: Option<ed25519::Ed25519Identity>,
            _certified_key: ed25519::Ed25519Identity,
            cert: KeyUnknownCert,
        ) -> StdResult<TimeRangeBound<Self>, VerifyFailed> {
            Self::verify(cert)
        }
    }
    impl Ed25519CertTest for Ed25519FamilyCert {
        fn new(
            signing_key: ed25519::Ed25519Identity,
            _certified_key: ed25519::Ed25519Identity,
        ) -> Self {
            Self {
                family_ed25519: signing_key,
            }
        }
        fn cert_type() -> CertType {
            CertType::FAMILY_V_IDENTITY
        }
        fn new_signed(
            signing_key: &ed25519::Keypair,
            certified_key: ed25519::Ed25519Identity,
            expiry: SystemTime,
        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
            Self::new_signed(signing_key, certified_key, expiry)
        }
        fn verify(
            _signing_key: Option<ed25519::Ed25519Identity>,
            certified_key: ed25519::Ed25519Identity,
            cert: KeyUnknownCert,
        ) -> StdResult<TimeRangeBound<Self>, VerifyFailed> {
            Self::verify(certified_key, cert)
        }
    }
    impl Ed25519CertTest for Ed25519NtorCrossCert {
        fn new(
            _signing_key: ed25519::Ed25519Identity,
            _certified_key: ed25519::Ed25519Identity,
        ) -> Self {
            Self::dangerous_new_unverified()
        }
        fn cert_type() -> CertType {
            CertType::NTOR_CC_IDENTITY
        }
        fn new_signed(
            signing_key: &ed25519::Keypair,
            certified_key: ed25519::Ed25519Identity,
            expiry: SystemTime,
        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
            Self::new_signed(&ExpandedKeypair::from(signing_key), certified_key, expiry)
        }
        fn verify(
            signing_key: Option<ed25519::Ed25519Identity>,
            certified_key: ed25519::Ed25519Identity,
            cert: KeyUnknownCert,
        ) -> StdResult<TimeRangeBound<Self>, VerifyFailed> {
            Self::verify(signing_key.unwrap(), certified_key, cert)
        }
    }
    /// Converts from [`Iso8601TimeSp`] to [`SystemTime`]
    fn str_to_st(s: &str) -> SystemTime {
        Iso8601TimeSp::from_str(s).unwrap().0
    }
    /// Tests a valid ad-hoc generated certificate.
    fn ed25519_cert_rng<T: Ed25519CertTest>() {
        let mut rng = testing_rng();
        let signing_key = ed25519::Keypair::generate(&mut rng);
        let certified_key = ed25519::Keypair::generate(&mut rng);
        let now = str_to_st("2000-01-01 06:00:00");
        let expiry = str_to_st("2000-01-01 12:00:00");
        // Test ad-hoc generation.
        let embedded_cert =
            T::new_signed(&signing_key, certified_key.public_key().into(), expiry).unwrap();
        assert_eq!(
            *embedded_cert.get().unwrap(),
            T::new(
                signing_key.public_key().into(),
                certified_key.public_key().into()
            )
        );
        // Verify ad-hoc certificate generation.
        let unverified = embedded_cert.raw_unverified().clone();
        assert_eq!(T::cert_type(), unverified.peek_cert_type());
        match unverified.peek_subject_key() {
            CertifiedKey::Ed25519(x) => assert_eq!(
                *x,
                ed25519::Ed25519Identity::from(certified_key.public_key())
            ),
            _ => panic!(),
        }
        // Finally, see if .verify() agrees.
        T::verify(
            Some(signing_key.public_key().into()),
            certified_key.public_key().into(),
            unverified.clone(),
        )
        .unwrap()
        .check_valid_at(&now)
        .unwrap();
        // See if .verify() also agrees when expired but with toleration.
        T::verify(
            Some(signing_key.public_key().into()),
            certified_key.public_key().into(),
            unverified,
        )
        .unwrap()
        .extend_end_bound(Duration::from_secs(60 * 60))
        .check_valid_at(&now)
        .unwrap();
    }
    /// Tests invalid Ed25519 certificates by violating various constraints.
    fn ed25519_cert_invalid<T: Ed25519CertTest + 'static>(requires_signed_with_ext: bool) {
        let mut rng = testing_rng();
        let now = str_to_st("2000-01-01 06:00:00");
        let expiry = str_to_st("2000-01-01 12:00:00");
        let signing_key = ed25519::Keypair::generate(&mut rng);
        let signing_pk = ed25519::Ed25519Identity::from(signing_key.public_key());
        let certified_key = ed25519::Keypair::generate(&mut rng);
        let certified_pk = ed25519::Ed25519Identity::from(certified_key.public_key());
        let mut tests: Vec<(_, _, CertifiedKey, _, _)> = vec![
            // Testing a violation of the signature is hard because the encoder
            // refuses to emit such a thing.
            // ---
            // Violate timestamp.
            (
                T::cert_type(),
                // We achieve this by setting expiry to now - 1 day.
                now - Duration::from_secs(64 * 64 * 24),
                certified_pk.into(),
                Some(&signing_pk),
                &signing_key,
            ),
            // Violate cert type.
            (
                // Just picking something completely out of place here.
                CertType::LINK_AUTH_X509,
                expiry,
                certified_pk.into(),
                Some(&signing_pk),
                &signing_key,
            ),
            // Violate certified key type.
            (
                T::cert_type(),
                expiry,
                // Just pass a different CertifiedKey variant here.
                CertifiedKey::RsaSha256Digest(certified_pk.into()),
                Some(&signing_pk),
                &signing_key,
            ),
            // ---
            // Missing test for violating both keys MUST be valid mappings to a
            // [`ed25519::PublicKey`].  I was unable to find a single test
            // vector for this, even in curve25591-dalek. :/
        ];
        // Violate absence of `signed-with-ed25519-key`.
        // This is not a violation in Ed25519NtorCrossCert.
        if requires_signed_with_ext {
            tests.push((
                T::cert_type(),
                expiry,
                certified_pk.into(),
                None,
                &signing_key,
            ));
        }
        for (ctype, expiry, certified_key, signing_key, signing_kp) in tests {
            let mut builder = Ed25519Cert::builder()
                .cert_type(ctype)
                .expiration(expiry)
                .cert_key(certified_key.clone())
                .clone();
            if let Some(signing_key) = signing_key {
                builder = builder.signing_key(*signing_key).clone();
            }
            let cert = Ed25519Cert::decode(&builder.encode_and_sign(signing_kp).unwrap()).unwrap();
            // We purposely always create an Ed25519Identity here from the bytes
            // in order to make it possible to test for invalid certified
            // key types.
            T::verify(
                signing_key.copied(),
                Ed25519Identity::from_bytes(certified_key.as_bytes()).unwrap(),
                cert,
            )
            .and_then(|expired| expired.check_valid_at(&now).map_err(|e| e.into()))
            .unwrap_err();
        }
    }
    #[test]
    fn ed25519_cert_rng_test() {
        ed25519_cert_rng::<Ed25519IdentityCert>();
        ed25519_cert_rng::<Ed25519FamilyCert>();
        ed25519_cert_rng::<Ed25519NtorCrossCert>();
    }
    #[test]
    fn ed25519_cert_invalid_test() {
        ed25519_cert_invalid::<Ed25519IdentityCert>(true);
        ed25519_cert_invalid::<Ed25519FamilyCert>(true);
        ed25519_cert_invalid::<Ed25519NtorCrossCert>(false);
    }
}