Skip to main content

tor_netdoc/types/
misc.rs

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
6pub use b16impl::*;
7pub use b64impl::*;
8pub use contact_info::*;
9pub use curve25519impl::*;
10pub use ed25519impl::*;
11pub use edcert::*;
12pub use fingerprint::*;
13pub use hostname::*;
14pub use rsa::*;
15pub use timeimpl::*;
16
17pub use nickname::{InvalidNickname, Nickname};
18
19pub use boolean::NumericBoolean;
20
21pub use fingerprint::{Base64Fingerprint, Fingerprint};
22
23pub use identified_digest::{DigestName, IdentifiedDigest};
24
25pub use ignored_impl::{
26    Ignored, IgnoredItemOrObjectValue, ItemPresent, NoMoreArguments, NotPresent,
27    NotPresentEachValue,
28};
29
30use crate::NormalItemArgument;
31use 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};
41use 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
54use derive_deftly::{Deftly, define_derive_deftly, define_derive_deftly_module};
55use digest::Digest as _;
56use educe::Educe;
57use std::cmp::{self, Ordering, PartialOrd};
58use std::fmt::{self, Display};
59use std::iter;
60use std::marker::PhantomData;
61use std::ops::{Deref, DerefMut};
62use std::result::Result as StdResult;
63use std::str::FromStr;
64use subtle::{Choice, ConstantTimeEq};
65use tor_error::{Bug, ErrorReport as _, internal, into_internal};
66use 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.
71pub(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    fn from_vec(v: Vec<u8>, p: crate::Pos) -> crate::Result<Self> {
77        Self::from_bytes(&v[..], p)
78    }
79}
80
81define_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<$tgens> From<$ftype> for $ttype {
103        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<$tgens> From<$ttype> for $ftype {
112        fn from(self_: $ttype) -> $ftype {
113            self_.$fname
114        }
115    }
116
117    impl<$tgens> Deref for $ttype {
118        type Target = $ftype;
119        fn deref(&self) -> &$ftype {
120            &self.$fname
121        }
122    }
123
124    impl<$tgens> DerefMut for $ttype {
125        fn deref_mut(&mut self) -> &mut $ftype {
126            &mut self.$fname
127        }
128    }
129
130    impl<$tgens> AsRef<$ftype> for $ttype {
131        fn as_ref(&self) -> &$ftype {
132            &self.$fname
133        }
134    }
135
136    impl<$tgens> AsMut<$ftype> for $ttype {
137        fn as_mut(&mut self) -> &mut $ftype {
138            &mut self.$fname
139        }
140    }
141  }}
142  }}
143}
144
145define_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
174define_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<$tgens> ConstantTimeEq for $ttype {
206        fn ct_eq(&self, other: &$ttype) -> Choice {
207          $(
208            self.$fname.ct_eq(&other.$fname)
209          )
210        }
211    }
212    $/// `$tname` is `Eq` via its constant-time implementation.
213    impl<$tgens> PartialEq for $ttype {
214        fn eq(&self, other: &$ttype) -> bool {
215            self.ct_eq(other).into()
216        }
217    }
218    impl<$tgens> Eq for $ttype {}
219    impl<$tgens> PartialOrd for $ttype {
220        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
221            Some(self.cmp(other))
222        }
223    }
224    impl<$tgens> Ord for $ttype {
225        fn cmp(&self, other: &Self) -> Ordering {
226          $(
227            self.$fname.cmp(&other.$fname)
228          )
229        }
230    }
231
232    impl<$tgens> $ttype {
233        /// Return the byte array from this object.
234        pub fn as_bytes(&self) -> &[u8] {
235          $(
236            &self.$fname[..]
237          )
238        }
239    }
240
241    impl<$tgens> AsRef<[u8]> for $ttype {
242        fn as_ref(&self) -> &[u8] {
243          $(
244            self.$fname.as_ref()
245          )
246        }
247    }
248
249    impl<$tgens> AsMut<[u8]> for $ttype {
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.
259mod 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        fn from_str(s: &str) -> Result<Self> {
279            let v: core::result::Result<Vec<u8>, base64ct::Error> = match s.len() % 4 {
280                0 => Base64::decode_vec(s),
281                _ => Base64Unpadded::decode_vec(s),
282            };
283            let v = v.map_err(|_| {
284                EK::BadArgument
285                    .with_msg("Invalid base64")
286                    .at_pos(Pos::at(s))
287            })?;
288            Ok(B64(v))
289        }
290    }
291
292    impl Display for B64 {
293        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
294            Display::fmt(&Base64Unpadded::encode_string(&self.0), f)
295        }
296    }
297
298    impl B64 {
299        /// Return this object if its length is within the provided bounds
300        /// object, or an error otherwise.
301        pub(crate) fn check_len<B: RangeBounds<usize>>(self, bounds: B) -> Result<Self> {
302            if bounds.contains(&self.0.len()) {
303                Ok(self)
304            } else {
305                Err(EK::BadObjectVal.with_msg("Invalid length on base64 data"))
306            }
307        }
308
309        /// Try to convert this object into an array of N bytes.
310        ///
311        /// Return an error if the length is wrong.
312        pub(crate) fn into_array<const N: usize>(self) -> Result<[u8; N]> {
313            self.0
314                .try_into()
315                .map_err(|_| EK::BadObjectVal.with_msg("Invalid length on base64 data"))
316        }
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        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340            Display::fmt(&B64(self.0.to_vec()), f)
341        }
342    }
343
344    impl<const N: usize> FromStr for FixedB64<N> {
345        type Err = Error;
346        fn from_str(s: &str) -> Result<Self> {
347            Ok(Self(B64::from_str(s)?.0.try_into().map_err(|_| {
348                EK::BadArgument
349                    .at_pos(Pos::at(s))
350                    .with_msg("invalid length")
351            })?))
352        }
353    }
354
355    impl<const N: usize> NormalItemArgument for FixedB64<N> {}
356}
357
358// ============================================================
359
360/// Types for decoding hex-encoded values.
361mod 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        fn from_str(s: &str) -> Result<Self> {
399            let bytes = hex::decode(s).map_err(|_| {
400                EK::BadArgument
401                    .at_pos(Pos::at(s))
402                    .with_msg("invalid hexadecimal")
403            })?;
404            Ok(B16(bytes))
405        }
406    }
407
408    impl FromStr for B16U {
409        type Err = Error;
410        fn from_str(s: &str) -> Result<Self> {
411            Ok(B16U(B16::from_str(s)?.0))
412        }
413    }
414
415    impl<const N: usize> FromStr for FixedB16U<N> {
416        type Err = Error;
417        fn from_str(s: &str) -> Result<Self> {
418            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        }
424    }
425
426    /// Write `b` to `f` in hex uppercase
427    // `hex` has `hex::encode_upper` but that allocates a `String`
428    fn write_b16u(b: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
429        for c in b {
430            write!(f, "{c:02X}")?;
431        }
432        Ok(())
433    }
434
435    impl Display for B16 {
436        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            for c in self.as_bytes() {
439                write!(f, "{c:02x}")?;
440            }
441            Ok(())
442        }
443    }
444
445    impl Display for B16U {
446        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
447            write_b16u(self.as_bytes(), f)
448        }
449    }
450
451    impl<const N: usize> Display for FixedB16U<N> {
452        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453            write_b16u(self.as_bytes(), f)
454        }
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
465mod 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        fn from_str(s: &str) -> Result<Self> {
481            let pk: FixedB64<32> = s.parse()?;
482            let pk: [u8; 32] = pk.into();
483            Ok(Curve25519Public(pk.into()))
484        }
485    }
486
487    impl Display for Curve25519Public {
488        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
489            FixedB64::from(self.0.to_bytes()).fmt(f)
490        }
491    }
492
493    impl NormalItemArgument for Curve25519Public {}
494}
495
496// ============================================================
497
498/// Types for decoding ed25519 keys
499mod 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        fn from_str(s: &str) -> Result<Self> {
516            let pk: FixedB64<32> = s.parse()?;
517            Ok(Ed25519Public(Ed25519Identity::new(pk.into())))
518        }
519    }
520
521    impl Display for Ed25519Public {
522        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
523            let pk: [u8; 32] = self.0.into();
524            let pk = FixedB64::from(pk);
525            pk.fmt(f)
526        }
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        fn from(pk: Ed25519Public) -> Self {
567            Self {
568                alg: Ed25519AlgorithmString::Ed25519,
569                pk,
570            }
571        }
572    }
573
574    impl From<Ed25519Identity> for Ed25519IdentityLine {
575        fn from(pk: Ed25519Identity) -> Self {
576            Ed25519Public(pk).into()
577        }
578    }
579
580    impl ItemArgument for Signature {
581        fn write_arg_onto(&self, out: &mut ItemEncoder) -> StdResult<(), Bug> {
582            FixedB64::from(self.to_bytes()).write_arg_onto(out)
583        }
584    }
585}
586
587// ============================================================
588
589/// Dummy types like [`Ignored`]
590mod 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        fn accumulate(self, _: &mut Option<NotPresent>, _: NotPresentEachValue) -> Result<(), EP> {
774            Ok(())
775        }
776        fn finish(self, _acc: Option<NotPresent>, _: &'static str) -> Result<NotPresent, EP> {
777            Ok(NotPresent)
778        }
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        fn parse_with<P>(self, _: &mut ArgumentStream<'_>, _: P) -> Result<Self::Field, AE>
795        where
796            P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result<Self::Each, AE>,
797        {
798            Ok(NotPresent)
799        }
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        fn resolve_option(self, _found: Option<NotPresentEachValue>) -> Result<NotPresent, EP> {
830            Ok(NotPresent)
831        }
832        fn debug_core(self) -> &'static str {
833            "NotPresent"
834        }
835    }
836    impl ItemObjectParseable for NotPresentEachValue {
837        fn check_label(_label: &str) -> Result<(), EP> {
838            Err(EP::ObjectUnexpected)
839        }
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        fn iter_ordered(self, _: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
849            iter::empty()
850        }
851    }
852
853    impl encode::OptionalityMethods for EMultiplicitySelector<NotPresent> {
854        type Field = NotPresent;
855        type Each = NotPresentEachValue;
856        fn as_option<'f>(self, _: &'f Self::Field) -> Option<&'f Self::Each> {
857            None
858        }
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        fn check_label(_label: &str) -> Result<(), EP> {
876            // allow any label
877            Ok(())
878        }
879        fn from_bytes(_input: &[u8]) -> Result<Self, EP> {
880            Ok(Ignored)
881        }
882    }
883
884    impl ObjectSetMethods for P2MultiplicitySelector<Ignored> {
885        type Field = Ignored;
886        type Each = Ignored;
887        fn resolve_option(self, _found: Option<Ignored>) -> Result<Ignored, EP> {
888            Ok(Ignored)
889        }
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        fn as_option<'f>(self, _: &'f Self::Field) -> Option<&'f Self::Each> {
907            None
908        }
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        fn from_args(args: &mut ArgumentStream) -> Result<NoMoreArguments, ArgumentError> {
928            Ok(args.reject_extra_args()?)
929        }
930    }
931
932    impl ItemArgument for NoMoreArguments {
933        fn write_arg_onto(&self, _: &mut ItemEncoder) -> Result<(), Bug> {
934            Ok(())
935        }
936    }
937
938    impl<T: Default> ItemValueParseable for ItemPresent<T> {
939        fn from_unparsed(item: UnparsedItem<'_>) -> StdResult<Self, EP> {
940            item.check_no_object()?;
941            Ok(Self::default())
942        }
943    }
944
945    impl<T: Default> ItemValueEncodable for ItemPresent<T> {
946        fn write_item_value_onto(&self, out: ItemEncoder) -> StdResult<(), Bug> {
947            out.finish();
948            Ok(())
949        }
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
984pub 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
993impl<T> Unknown<T> {
994    /// Create an `Unknown` which specifies that values were discarded (or should be)
995    pub fn new_discard() -> Self {
996        Unknown::Discarded(PhantomData)
997    }
998
999    /// Map the `Retained`, if there is one
1000    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Unknown<U> {
1001        self.try_map(move |t| Ok::<_, Void>(f(t))).void_unwrap()
1002    }
1003
1004    /// Map the `Retained`, fallibly
1005    pub fn try_map<U, E>(self, f: impl FnOnce(T) -> Result<U, E>) -> Result<Unknown<U>, E> {
1006        Ok(match self {
1007            Unknown::Discarded(_) => Unknown::Discarded(PhantomData),
1008            #[cfg(feature = "retain-unknown")]
1009            Unknown::Retained(t) => Unknown::Retained(f(t)?),
1010        })
1011    }
1012
1013    /// Obtain an `Unknown` containing (maybe) a reference
1014    pub fn as_ref(&self) -> Unknown<&T> {
1015        match self {
1016            Unknown::Discarded(_) => Unknown::Discarded(PhantomData),
1017            #[cfg(feature = "retain-unknown")]
1018            Unknown::Retained(t) => Unknown::Retained(t),
1019        }
1020    }
1021
1022    /// Return the retained unknown data, giving `None` if none was saved
1023    ///
1024    /// This is the function for disregarding the possible previously existence
1025    /// of now-discarded unknown (unrecognised) information.
1026    ///
1027    /// Use [`into_retained`](Self::into_retained) if it would be a bug
1028    /// if unrecognised information had been previously discarded.
1029    pub fn only_known(self) -> Option<T> {
1030        match self {
1031            Unknown::Discarded(_) => None,
1032            #[cfg(feature = "retain-unknown")]
1033            Unknown::Retained(t) => Some(t),
1034        }
1035    }
1036
1037    /// Obtain the `Retained` data
1038    ///
1039    /// Treats lack of retention as an internal error.
1040    pub fn into_retained(self) -> Result<T, Bug> {
1041        match self {
1042            Unknown::Discarded(_) => Err(internal!("Unknown::retained but data not collected")),
1043            #[cfg(feature = "retain-unknown")]
1044            Unknown::Retained(t) => Ok(t),
1045        }
1046    }
1047
1048    /// Start recording unknown information, with a default value for `T`
1049    #[cfg(feature = "retain-unknown")]
1050    pub fn new_retained_default() -> Self
1051    where
1052        T: Default,
1053    {
1054        Unknown::Retained(T::default())
1055    }
1056
1057    /// Update the `Retained`, if there is one
1058    ///
1059    /// Intended for use in parsing, when we encounter an unknown value.
1060    ///
1061    /// Not provided in `try_` form.  If you think you need this, instead, unconditionally
1062    /// parse and verify the unknown value, and then conditionally insert it with this function.
1063    /// Don't parse it conditionally - that would skip some validation.
1064    pub fn with_mut_unknown(&mut self, f: impl FnOnce(&mut T)) {
1065        match self {
1066            Unknown::Discarded(_) => {}
1067            #[cfg(feature = "retain-unknown")]
1068            Unknown::Retained(t) => f(t),
1069        }
1070    }
1071}
1072
1073impl<T: PartialOrd> PartialOrd for Unknown<T> {
1074    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1075        use Unknown::*;
1076        match (self, other) {
1077            (Discarded(_), Discarded(_)) => Some(cmp::Ordering::Equal),
1078            #[cfg(feature = "retain-unknown")]
1079            (Discarded(_), Retained(_)) | (Retained(_), Discarded(_)) => None,
1080            #[cfg(feature = "retain-unknown")]
1081            (Retained(a), Retained(b)) => a.partial_cmp(b),
1082        }
1083    }
1084}
1085
1086// ============================================================
1087
1088/// A finite floating point number
1089///
1090/// Suitable for `stats` items in voites' routerstatus entries:
1091/// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:stats>
1092///
1093/// Invariants:
1094///
1095///  * Is finite.  (So not NaN or Inf.)  Might be denormal.
1096///
1097/// String representation:
1098///
1099///  * Parses any valid C-like notation.
1100///
1101///  * Never uses exponential notation to display.
1102///
1103///  * Output can be rather large, up to 326 characters!
1104///    This is a spec bug.  The spec forbids us from using exponential notation.
1105///    <https://gitlab.torproject.org/tpo/core/torspec/-/work_items/416>
1106///
1107/// We may to change this in the future to use exponentials notation for output.
1108/// See <https://gitlab.torproject.org/tpo/core/torspec/-/work_items/416>
1109///
1110/// Comparison and ordering:
1111///
1112/// Implements `Eq` and `Ord` according to *mathematical* equality and ordering.
1113/// In particular, `-0.0` compares equal to `0.0`.
1114/// Therefore, does not implement `Hash`.
1115//
1116// TODO torspec#416 Consider replacing our F64Finite with finite f64 newtype from some crate
1117//
1118// What a palaver!
1119//
1120// This type is here rather than in rs.rs, in case similar things appears in other documents.
1121#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] //
1122#[derive(derive_more::Deref, derive_more::Into, derive_more::Display)]
1123pub struct F64Finite(f64);
1124
1125/// Error converting an [`F64Finite`] from an `f64`: the value wasn't finite
1126#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error, amplify::Getters)]
1127#[error("FP value {} ({bits:#x}) is not finite", f64::from_bits(self.bits))]
1128pub struct F64FiniteError {
1129    /// The raw bits (as from [`f64::to_bits`])
1130    //
1131    // We store it this way rather than as `f64` so that `Eq` etc. make sense.
1132    bits: u64,
1133}
1134
1135impl TryFrom<f64> for F64Finite {
1136    type Error = F64FiniteError;
1137
1138    fn try_from(v: f64) -> Result<Self, F64FiniteError> {
1139        v.is_finite()
1140            .then_some(F64Finite(v))
1141            .ok_or_else(|| F64FiniteError { bits: v.to_bits() })
1142    }
1143}
1144
1145/// Error parsing [`F64Finite`] from a string
1146#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
1147#[non_exhaustive]
1148pub enum F64FiniteParseError {
1149    /// Syntax error
1150    #[error("syntax error")]
1151    Syntax(#[from] std::num::ParseFloatError),
1152
1153    /// Value is not finite
1154    #[error("bad value")]
1155    NotFinite(#[from] F64FiniteError),
1156}
1157
1158impl FromStr for F64Finite {
1159    type Err = F64FiniteParseError;
1160
1161    fn from_str(s: &str) -> StdResult<Self, F64FiniteParseError> {
1162        Ok(s.parse::<f64>()?.try_into()?)
1163    }
1164}
1165
1166impl Eq for F64Finite {}
1167
1168#[allow(clippy::derive_ord_xor_partial_ord)]
1169impl Ord for F64Finite {
1170    fn cmp(&self, other: &F64Finite) -> cmp::Ordering {
1171        self.0
1172            .partial_cmp(&other.0)
1173            .expect("finite f64 partial_cmp gave None")
1174    }
1175}
1176
1177impl NormalItemArgument for F64Finite {}
1178
1179// ============================================================
1180
1181/// Known keyword (enum) value, or arbitrary string
1182///
1183/// `T` should be a `Copy` enum with unit variants.
1184/// It should have appropriate `FromStr` and `Display`,
1185/// as well as [`NormalItemArgument`], impls.
1186///
1187/// Then `KeywordOrString` will implement the same traits.
1188///
1189/// Unlike [`Unknown`], unknown values are always retained as strings.
1190//
1191// `RelayFlags` has machinery for parsing flags and retaining unknown values,
1192// but it uses `Unknown` to maybe discard unknown flags,
1193// and it is generally quite a lot more complicated.
1194#[derive(Debug, PartialEq, Clone, Hash)]
1195#[allow(clippy::exhaustive_enums)] // this isn't going to change
1196pub enum KeywordOrString<T: Copy> {
1197    /// Known and recognised `T`
1198    Known(T),
1199
1200    /// Unknown value in arbitrary syntax
1201    Unknown(String),
1202}
1203
1204impl<T: Copy + NormalItemArgument> NormalItemArgument for KeywordOrString<T> {}
1205
1206impl<T: Copy + Display> Display for KeywordOrString<T> {
1207    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1208        match self {
1209            KeywordOrString::Known(t) => Display::fmt(t, f),
1210            KeywordOrString::Unknown(s) => Display::fmt(s, f),
1211        }
1212    }
1213}
1214
1215impl<T: Copy + FromStr> FromStr for KeywordOrString<T> {
1216    type Err = Void;
1217    fn from_str(s: &str) -> Result<Self, Void> {
1218        Ok(match s.parse() {
1219            Ok(y) => KeywordOrString::Known(y),
1220            Err(_) => KeywordOrString::Unknown(s.to_owned()),
1221        })
1222    }
1223}
1224
1225// ============================================================
1226
1227/// A sequence of `T` items, with their order retained
1228///
1229/// Normally when a `Vec<T>` appears in a network document,
1230/// we expect the items to be sortable - they must impl [`EncodeOrd`](encode::EncodeOrd).
1231/// When encoding, the output is always sorted.
1232///
1233/// *This* type retains the ordering.
1234///
1235/// Implements the [`encode`] and [`parse2`] item multiplicity traits.
1236#[derive(Debug, Clone, Hash, Deftly, Eq, PartialEq, Ord, PartialOrd, Educe)]
1237#[educe(Default)]
1238#[derive_deftly(Transparent)]
1239#[allow(clippy::exhaustive_structs)]
1240pub struct RetainedOrderVec<T>(pub Vec<T>);
1241
1242// ============================================================
1243
1244/// Types for decoding times and dates
1245mod timeimpl {
1246    use super::*;
1247    use crate::{Error, NetdocErrorKind as EK, Pos, Result};
1248    use std::time::SystemTime;
1249    use time::{
1250        OffsetDateTime, PrimitiveDateTime, format_description::FormatItem,
1251        macros::format_description,
1252    };
1253
1254    /// A wall-clock time, encoded in Iso8601 format with an intervening
1255    /// space between the date and time.
1256    ///
1257    /// (Example: "2020-10-09 17:38:12")
1258    #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Deftly)]
1259    #[derive_deftly(Transparent)]
1260    #[allow(clippy::exhaustive_structs)]
1261    pub struct Iso8601TimeSp(pub SystemTime);
1262
1263    /// Formatting object for parsing the space-separated Iso8601 format.
1264    const ISO_8601SP_FMT: &[FormatItem] =
1265        format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
1266
1267    impl FromStr for Iso8601TimeSp {
1268        type Err = Error;
1269        fn from_str(s: &str) -> Result<Iso8601TimeSp> {
1270            let d = PrimitiveDateTime::parse(s, &ISO_8601SP_FMT).map_err(|e| {
1271                EK::BadArgument
1272                    .at_pos(Pos::at(s))
1273                    .with_msg(format!("invalid time: {}", e))
1274            })?;
1275            Ok(Iso8601TimeSp(d.assume_utc().into()))
1276        }
1277    }
1278
1279    /// Formats a SystemTime according to the given format description
1280    ///
1281    /// Also converts any time::error::format to fmt::Error
1282    /// so that it can be unwrapped in the Display trait impl
1283    fn fmt_with(
1284        t: SystemTime,
1285        format_desc: &[FormatItem],
1286    ) -> core::result::Result<String, fmt::Error> {
1287        OffsetDateTime::from(t)
1288            .format(format_desc)
1289            .map_err(|_| fmt::Error)
1290    }
1291
1292    impl Display for Iso8601TimeSp {
1293        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1294            write!(f, "{}", fmt_with(self.0, ISO_8601SP_FMT)?)
1295        }
1296    }
1297
1298    /// A wall-clock time, encoded in ISO8601 format without an intervening
1299    /// space.
1300    ///
1301    /// This represents a specific UTC instant (ie an instant in global civil time).
1302    /// But it may not be able to represent leap seconds.
1303    ///
1304    /// The timezone is not included in the string representation; `+0000` is implicit.
1305    ///
1306    /// # Example
1307    ///
1308    /// ```
1309    /// use tor_netdoc::types::Iso8601TimeNoSp;
1310    ///
1311    /// let s = "2020-10-09T17:38:12";
1312    /// let t: Iso8601TimeNoSp = s.parse().unwrap();
1313    /// assert_eq!(t.to_string(), s);
1314    /// ```
1315    #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Deftly)]
1316    #[derive_deftly(Transparent)]
1317    #[allow(clippy::exhaustive_structs)]
1318    pub struct Iso8601TimeNoSp(pub SystemTime);
1319
1320    /// Formatting object for parsing the space-separated Iso8601 format.
1321    const ISO_8601NOSP_FMT: &[FormatItem] =
1322        format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]");
1323
1324    impl FromStr for Iso8601TimeNoSp {
1325        type Err = Error;
1326        fn from_str(s: &str) -> Result<Iso8601TimeNoSp> {
1327            let d = PrimitiveDateTime::parse(s, &ISO_8601NOSP_FMT).map_err(|e| {
1328                EK::BadArgument
1329                    .at_pos(Pos::at(s))
1330                    .with_msg(format!("invalid time: {}", e))
1331            })?;
1332            Ok(Iso8601TimeNoSp(d.assume_utc().into()))
1333        }
1334    }
1335
1336    impl Display for Iso8601TimeNoSp {
1337        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1338            write!(f, "{}", fmt_with(self.0, ISO_8601NOSP_FMT)?)
1339        }
1340    }
1341
1342    impl crate::NormalItemArgument for Iso8601TimeNoSp {}
1343}
1344
1345/// Types for decoding RSA keys
1346mod rsa {
1347    use super::*;
1348    use crate::{NetdocErrorKind as EK, Pos, Result};
1349    use std::ops::RangeBounds;
1350    use tor_llcrypto::pk::rsa::PublicKey;
1351    use tor_llcrypto::{d::Sha1, pk::rsa::KeyPair};
1352
1353    /// The fixed exponent which we require when parsing any RSA key in a netdoc
1354    //
1355    // TODO this value is duplicated a lot in the v1 parser
1356    pub(crate) const RSA_FIXED_EXPONENT: u32 = 65537;
1357
1358    /// The fixed exponent which we require when parsing any RSA key in a netdoc
1359    //
1360    // TODO this value is duplicated a lot in the v1 parser
1361    pub(crate) const RSA_MIN_BITS: usize = 1024;
1362
1363    /// RSA public key, partially processed by `crate::paarse`.
1364    ///
1365    /// As parsed from a base64-encoded object.
1366    /// They key's properties (exponent and size) haven't been checked.
1367    #[allow(non_camel_case_types)]
1368    #[derive(Clone, Debug)]
1369    pub(crate) struct RsaPublicParse1Helper(PublicKey, Pos);
1370
1371    /// RSA signature using SHA-1 as per "Signing documents" in dir-spec
1372    ///
1373    /// <https://spec.torproject.org/dir-spec/netdoc.html#signing>
1374    ///
1375    /// Used for
1376    /// [`AuthCert::dir-key-certification`](crate::doc::authcert::AuthCert::dir-key-certification),
1377    /// for example.
1378    ///
1379    /// # Caveats
1380    ///
1381    /// This type MUST NOT be used for anomalous signatures
1382    /// such as
1383    /// [`AuthCert::dir_key_crosscert`](crate::doc::authcert::AuthCert::dir_key_crosscert);
1384    /// in that case because `dir_key_crosscert`'s
1385    /// set of allowed object labels includes `ID SIGNATURE` whereas this type
1386    /// is always `SIGNATURE`
1387    #[derive(Debug, Clone, PartialEq, Eq, Deftly)]
1388    #[derive_deftly(ItemValueParseable, ItemValueEncodable)]
1389    #[deftly(netdoc(no_extra_args, signature(hash_accu = Sha1WholeKeywordLine)))]
1390    #[non_exhaustive]
1391    pub struct RsaSha1Signature {
1392        /// The bytes of the signature (base64-decoded).
1393        #[deftly(netdoc(object(label = "SIGNATURE"), with = crate::types::raw_data_object))]
1394        pub signature: Vec<u8>,
1395    }
1396
1397    impl From<RsaPublicParse1Helper> for PublicKey {
1398        fn from(k: RsaPublicParse1Helper) -> PublicKey {
1399            k.0
1400        }
1401    }
1402    impl super::FromBytes for RsaPublicParse1Helper {
1403        fn from_bytes(b: &[u8], pos: Pos) -> Result<Self> {
1404            let key = PublicKey::from_der(b)
1405                .ok_or_else(|| EK::BadObjectVal.with_msg("unable to decode RSA public key"))?;
1406            Ok(RsaPublicParse1Helper(key, pos))
1407        }
1408    }
1409    impl RsaPublicParse1Helper {
1410        /// Give an error if the exponent of this key is not 'e'
1411        pub(crate) fn check_exponent(self, e: u32) -> Result<Self> {
1412            if self.0.exponent_is(e) {
1413                Ok(self)
1414            } else {
1415                Err(EK::BadObjectVal
1416                    .at_pos(self.1)
1417                    .with_msg("invalid RSA exponent"))
1418            }
1419        }
1420        /// Give an error if the length of this key's modulus, in
1421        /// bits, is not contained in 'bounds'
1422        pub(crate) fn check_len<B: RangeBounds<usize>>(self, bounds: B) -> Result<Self> {
1423            if bounds.contains(&self.0.bits()) {
1424                Ok(self)
1425            } else {
1426                Err(EK::BadObjectVal
1427                    .at_pos(self.1)
1428                    .with_msg("invalid RSA length"))
1429            }
1430        }
1431        /// Give an error if the length of this key's modulus, in
1432        /// bits, is not exactly `n`.
1433        pub(crate) fn check_len_eq(self, n: usize) -> Result<Self> {
1434            self.check_len(n..=n)
1435        }
1436    }
1437
1438    impl RsaSha1Signature {
1439        /// Make a signature according to "Signing documents" in the netdoc spec
1440        ///
1441        /// <https://spec.torproject.org/dir-spec/netdoc.html#signing>
1442        ///
1443        /// `NetdocEncoder` should have had the body of the document
1444        /// (everything except the signatures) already encoded.
1445        ///
1446        /// `item_keyword` is the keyword for the signature item.
1447        /// This is needed because different documents use different keywords,
1448        /// and the keyword is covered by the signature (an annoying is a layering violation).
1449        /// See <https://gitlab.torproject.org/tpo/core/torspec/-/issues/322>.
1450        ///
1451        /// # Example
1452        ///
1453        /// ```
1454        /// use derive_deftly::Deftly;
1455        /// use tor_error::Bug;
1456        /// use tor_llcrypto::pk::rsa;
1457        /// use tor_netdoc::derive_deftly_template_NetdocEncodable;
1458        /// use tor_netdoc::encode::{NetdocEncodable, NetdocEncoder};
1459        /// use tor_netdoc::types::RsaSha1Signature;
1460        ///
1461        /// #[derive(Deftly, Default)]
1462        /// #[derive_deftly(NetdocEncodable)]
1463        /// pub struct Document {
1464        ///     pub document_intro_keyword: (),
1465        /// }
1466        /// #[derive(Deftly)]
1467        /// #[derive_deftly(NetdocEncodable)]
1468        /// pub struct DocumentSignatures {
1469        ///     pub document_signature: RsaSha1Signature,
1470        /// }
1471        /// impl Document {
1472        ///     pub fn encode_sign(&self, k: &rsa::KeyPair) -> Result<String, Bug> {
1473        ///         let mut encoder = NetdocEncoder::new();
1474        ///         self.encode_unsigned(&mut encoder)?;
1475        ///         let document_signature =
1476        ///             RsaSha1Signature::new_sign_netdoc(k, &encoder, "document-signature")?;
1477        ///         let sigs = DocumentSignatures { document_signature };
1478        ///         sigs.encode_unsigned(&mut encoder)?;
1479        ///         let encoded = encoder.finish()?;
1480        ///         Ok(encoded)
1481        ///     }
1482        /// }
1483        ///
1484        /// # fn main() -> Result<(), anyhow::Error> {
1485        /// let k = rsa::KeyPair::generate(&mut tor_basic_utils::test_rng::testing_rng())?;
1486        /// let doc = Document::default();
1487        /// let encoded = doc.encode_sign(&k)?;
1488        /// assert!(encoded.starts_with(concat!(
1489        ///     "document-intro-keyword\n",
1490        ///     "document-signature\n",
1491        ///     "-----BEGIN SIGNATURE-----\n",
1492        /// )));
1493        /// # Ok(())
1494        /// # }
1495        /// ```
1496        pub fn new_sign_netdoc(
1497            private_key: &KeyPair,
1498            encoder: &NetdocEncoder,
1499            item_keyword: &str,
1500        ) -> StdResult<Self, Bug> {
1501            let mut h = Sha1::new();
1502            h.update(encoder.text_sofar()?);
1503            h.update(item_keyword);
1504            h.update("\n");
1505            let h = h.finalize();
1506            let signature = private_key
1507                .sign(&h)
1508                .map_err(into_internal!("RSA signing failed"))?;
1509            Ok(RsaSha1Signature { signature })
1510        }
1511    }
1512}
1513
1514/// Types for decoding Ed25519 certificates
1515mod edcert {
1516    use std::result::Result as StdResult;
1517    use std::time::SystemTime;
1518
1519    use crate::types::EmbeddedCert;
1520    use crate::{
1521        NetdocErrorKind as EK, Pos, Result,
1522        parse2::{ErrorProblem, VerifyFailed},
1523        types::EmbeddableCertObject,
1524    };
1525    use tor_cert::{CertType, CertifiedKey, Ed25519Cert, KeyUnknownCert};
1526    use tor_checkable::signed::SignatureGated;
1527    use tor_checkable::timed::TimeRangeBound;
1528    use tor_checkable::{SelfSigned, TimeBound};
1529    use tor_error::{Bug, into_internal};
1530    use tor_llcrypto::pk::ed25519::{self, Ed25519PublicKey, ValidatableEd25519Signature};
1531
1532    /// An ed25519 certificate as parsed from a directory object, with
1533    /// signature not validated.
1534    #[derive(Debug, Clone)]
1535    pub(crate) struct UnvalidatedEdCert(KeyUnknownCert, Pos);
1536
1537    impl super::FromBytes for UnvalidatedEdCert {
1538        fn from_bytes(b: &[u8], p: Pos) -> Result<Self> {
1539            let cert = Ed25519Cert::decode(b).map_err(|e| {
1540                EK::BadObjectVal
1541                    .at_pos(p)
1542                    .with_msg("Bad certificate")
1543                    .with_source(e)
1544            })?;
1545
1546            Ok(Self(cert, p))
1547        }
1548        fn from_vec(v: Vec<u8>, p: Pos) -> Result<Self> {
1549            Self::from_bytes(&v[..], p)
1550        }
1551    }
1552    impl UnvalidatedEdCert {
1553        /// Give an error if this certificate's type is not `desired_type`.
1554        pub(crate) fn check_cert_type(self, desired_type: CertType) -> Result<Self> {
1555            if self.0.peek_cert_type() != desired_type {
1556                return Err(EK::BadObjectVal.at_pos(self.1).with_msg(format!(
1557                    "bad certificate type {} (wanted {})",
1558                    self.0.peek_cert_type(),
1559                    desired_type
1560                )));
1561            }
1562            Ok(self)
1563        }
1564        /// Give an error if this certificate's subject_key is not `pk`
1565        pub(crate) fn check_subject_key_is(self, pk: &ed25519::Ed25519Identity) -> Result<Self> {
1566            if self.0.peek_subject_key().as_ed25519() != Some(pk) {
1567                return Err(EK::BadObjectVal
1568                    .at_pos(self.1)
1569                    .with_msg("incorrect subject key"));
1570            }
1571            Ok(self)
1572        }
1573        /// Consume this object and return the inner Ed25519 certificate.
1574        pub(crate) fn into_unchecked(self) -> KeyUnknownCert {
1575            self.0
1576        }
1577    }
1578
1579    /// An Ed25519 identity certificate.
1580    ///
1581    /// This is a certificate of [`CertType::IDENTITY_V_SIGNING`] where the
1582    /// relay's long-term ed25519 identity key signs the relay's medium-term
1583    /// ed25519 signing key, used for signing almost all other certifications
1584    /// associated with a given relay.
1585    #[derive(Debug, Clone, PartialEq, Eq)]
1586    #[allow(clippy::exhaustive_structs)]
1587    pub struct Ed25519IdentityCert {
1588        /// The long-term ed25519 identity key of the relay
1589        pub id_ed25519: ed25519::Ed25519Identity,
1590        /// The medium-term ed25519 signing key of the relay.
1591        pub sign_ed25519: ed25519::Ed25519Identity,
1592    }
1593
1594    impl EmbeddableCertObject<KeyUnknownCert> for Ed25519IdentityCert {
1595        const LABEL: &str = "ED25519 CERT";
1596    }
1597
1598    impl Ed25519IdentityCert {
1599        /// Verifies the validity of an [`Ed25519IdentityCert`].
1600        ///
1601        /// # Requirements
1602        ///
1603        /// 1. MUST have the identity key in the `signed-with-ed25519-key` extension.
1604        /// 2. MUST have a valid signature by the identity key.
1605        /// 3. MUST be of [`CertType::IDENTITY_V_SIGNING`].
1606        /// 4. Certified key MUST BE of [`tor_cert::CertifiedKey::Ed25519`].
1607        /// 5. Both keys MUST be valid mappings to a [`ed25519::PublicKey`].
1608        pub fn verify(cert: KeyUnknownCert) -> StdResult<TimeRangeBound<Self>, VerifyFailed> {
1609            let cert = cert
1610                // 1. MUST have the identity key in the `signed-with-ed25519-key` extension.
1611                .should_have_signing_key()
1612                .map_err(|_| VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData))?
1613                // 2. MUST have a valid signature by the identity key.
1614                .check_signature()?
1615                // Okay to call because we create TimeRangeBound later.
1616                // TODO DIRAUTH: Use TimeRangeBound instead.
1617                .dangerously_assume_timely();
1618
1619            // 3. MUST be of [`CertType::IDENTITY_V_SIGNING`].
1620            if cert.cert_type() != CertType::IDENTITY_V_SIGNING {
1621                return Err(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData));
1622            }
1623
1624            // Bug is alright because .should_have_signing_key() assured us.
1625            let id_ed25519 = *cert.signing_key().ok_or(VerifyFailed::Bug)?;
1626
1627            // 4. Certified key MUST BE of [`tor_cert::CertifiedKey::Ed25519`].
1628            let sign_ed25519 = *cert
1629                .subject_key()
1630                .as_ed25519()
1631                .ok_or(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData))?;
1632
1633            // 5. Both keys MUST be valid mappings to a [`ed25519::PublicKey`].
1634            // Unsure if this check is required or implied by (2) but defensive
1635            // programming does not hurt.
1636            if ed25519::PublicKey::try_from(id_ed25519).is_err()
1637                || ed25519::PublicKey::try_from(sign_ed25519).is_err()
1638            {
1639                return Err(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData));
1640            }
1641
1642            Ok(TimeRangeBound::new(
1643                Self {
1644                    id_ed25519,
1645                    sign_ed25519,
1646                },
1647                ..cert.expiry(),
1648            ))
1649        }
1650
1651        /// Creates a new signed [`Ed25519IdentityCert`].
1652        pub fn new_signed(
1653            id_ed25519: &ed25519::Keypair,
1654            sign_ed25519: ed25519::Ed25519Identity,
1655            expiry: SystemTime,
1656        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
1657            let cert = Ed25519Cert::builder()
1658                .expiration(expiry)
1659                .signing_key(id_ed25519.public_key().into())
1660                .cert_type(CertType::IDENTITY_V_SIGNING)
1661                .cert_key(sign_ed25519.into())
1662                .encode_and_sign(id_ed25519)
1663                .map_err(into_internal!("failed to encode and sign identity cert"))?;
1664
1665            let cert =
1666                Ed25519Cert::decode(&cert).map_err(into_internal!("decode just encoded cert"))?;
1667
1668            Ok(EmbeddedCert::new(
1669                Self {
1670                    id_ed25519: id_ed25519.public_key().into(),
1671                    sign_ed25519,
1672                },
1673                cert,
1674            ))
1675        }
1676    }
1677
1678    /// An Ed25519 family certificate.
1679    ///
1680    /// This is a certificate of [`CertType::FAMILY_V_IDENTITY`] where the
1681    /// family key signs the long-term ed25519 identity key of the given relay.
1682    ///
1683    /// It purposely does not store the long-term ed25519 identity key of the
1684    /// relay because the idea of this type should be equal only to other types
1685    /// with the same family key.
1686    #[derive(Debug, Clone, PartialEq, Eq)]
1687    #[allow(clippy::exhaustive_structs)]
1688    pub struct Ed25519FamilyCert {
1689        /// The public key of the family.
1690        // TODO: We probably want to add a getter for this returning the
1691        // family name as in:
1692        // <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:family-cert>
1693        pub family_ed25519: ed25519::Ed25519Identity,
1694    }
1695
1696    impl EmbeddableCertObject<KeyUnknownCert> for Ed25519FamilyCert {
1697        const LABEL: &str = "FAMILY CERT";
1698    }
1699
1700    impl Ed25519FamilyCert {
1701        /// Verifies the validity of an [`Ed25519FamilyCert`].
1702        ///
1703        /// For such a certificate to be valid, the caller must provide a
1704        /// known Ed25519 identity key of the relay beforehand.
1705        ///
1706        /// # Requirements
1707        ///
1708        /// 1. MUST have the `signed-with-ed25519-key` extension containing the family key.
1709        /// 2. MUST have a valid signature by the family key.
1710        /// 3. MUST be of of [`CertType::FAMILY_V_IDENTITY`].
1711        /// 4. Certified key MUST BE of [`tor_cert::CertifiedKey::Ed25519`].
1712        /// 5. `id_ed25519` MUST be the certified key.
1713        /// 6. Both keys MUST be valid mappings to a [`ed25519::PublicKey`].
1714        pub fn verify(
1715            id_ed25519: ed25519::Ed25519Identity,
1716            cert: KeyUnknownCert,
1717        ) -> StdResult<TimeRangeBound<Self>, VerifyFailed> {
1718            let cert = cert
1719                // 1. MUST have the `signed-with-ed25519-key` extension containing the family key.
1720                .should_have_signing_key()?
1721                // 2. MUST have a valid signature by the family key.
1722                .check_signature()?
1723                // Okay to call because we create TimeRangeBound later.
1724                // TODO DIRAUTH: Use TimeRangeBound instead.
1725                .dangerously_assume_timely();
1726
1727            // 3. MUST be of of [`CertType::FAMILY_V_IDENTITY`].
1728            if cert.cert_type() != CertType::FAMILY_V_IDENTITY {
1729                return Err(ErrorProblem::ObjectInvalidData.into());
1730            }
1731
1732            // Bug is alright because .should_have_signing_key() assured us.
1733            let family_ed25519 = *cert.signing_key().ok_or(VerifyFailed::Bug)?;
1734
1735            // 4. Certified key MUST BE of [`tor_cert::CertifiedKey::Ed25519`].
1736            let certified_key = *cert
1737                .subject_key()
1738                .as_ed25519()
1739                .ok_or(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData))?;
1740
1741            // 5. `id_ed25519` MUST be the certified key.
1742            if certified_key != id_ed25519 {
1743                return Err(VerifyFailed::VerifyFailed);
1744            }
1745
1746            // 6. Both keys MUST be valid mappings to a [`ed25519::PublicKey`].
1747            if ed25519::PublicKey::try_from(family_ed25519).is_err()
1748                || ed25519::PublicKey::try_from(id_ed25519).is_err()
1749            {
1750                return Err(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData));
1751            }
1752
1753            Ok(TimeRangeBound::new(
1754                Self { family_ed25519 },
1755                ..cert.expiry(),
1756            ))
1757        }
1758
1759        /// Creates a new signed [`Ed25519FamilyCert`].
1760        pub fn new_signed(
1761            family_ed25519: &ed25519::Keypair,
1762            id_ed25519: ed25519::Ed25519Identity,
1763            expiry: SystemTime,
1764        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
1765            let cert = Ed25519Cert::builder()
1766                .expiration(expiry)
1767                .signing_key(family_ed25519.public_key().into())
1768                .cert_type(CertType::FAMILY_V_IDENTITY)
1769                .cert_key(id_ed25519.into())
1770                .encode_and_sign(family_ed25519)
1771                .map_err(into_internal!("failed to encode and sign family cert"))?;
1772
1773            let cert =
1774                Ed25519Cert::decode(&cert).map_err(into_internal!("decode just encoded cert"))?;
1775
1776            Ok(EmbeddedCert::new(
1777                Self {
1778                    family_ed25519: family_ed25519.public_key().into(),
1779                },
1780                cert,
1781            ))
1782        }
1783    }
1784
1785    /// Verified reverse cert by K_ntor on KP_relayid_ed
1786    ///
1787    /// This certificate is signed by KS_ntor
1788    /// (the circuit extension key) and certifies
1789    /// KP_relayid_ed25519 ed25519 identity key of the relay.
1790    ///
1791    /// The type itself is zero-sized because it provides no new useful
1792    /// information that cannot be found elsewhere within the router descriptor.
1793    /// It is intended for use within
1794    /// [`EmbeddedCert`]`<Ed25519NtorCrossCert, KeyUnknownCert>`
1795    ///
1796    /// # Note on key conversion
1797    ///
1798    /// Keep in mind however that the ntor onion key is only provided as an
1799    /// X25519 key and *not* an Ed25519 key, meaning that interfacing
1800    /// applications have to convert it using a function such as
1801    /// [`tor_llcrypto::pk::keymanip::convert_curve25519_to_ed25519_public()`].
1802    /// This also requires obtaining the sign bit which is usually given as an
1803    /// argument in the `ntor-onion-key-crosscert` item.  However, this is
1804    /// outside of the scope of this struct and the code will assume that
1805    /// callers have already converted the X25519 public key to an Ed25519
1806    /// public key as outlined in the specifications.
1807    ///
1808    /// # See Also
1809    ///
1810    /// * <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:ntor-onion-key-crosscert>
1811    /// * <https://spec.torproject.org/dir-spec/converting-to-ed25519.html>
1812    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1813    #[non_exhaustive]
1814    pub struct Ed25519NtorCrossCert {
1815        /// Explicit field, to avoid constructing this accidentally without
1816        /// doing all the verification.
1817        _promise_we_verified: (),
1818    }
1819
1820    impl EmbeddableCertObject<KeyUnknownCert> for Ed25519NtorCrossCert {
1821        const LABEL: &str = "ED25519 CERT";
1822    }
1823
1824    impl Ed25519NtorCrossCert {
1825        /// Verifies the validity of an [`Ed25519NtorCrossCert`].
1826        ///
1827        /// For such a certificate to be valid, the caller must provide a known
1828        /// Ed25519 identity key and Ed25519 ntor onion key of the relay
1829        /// beforehand.
1830        ///
1831        /// # Requirements
1832        ///
1833        /// 1. MUST be of [`CertType::NTOR_CC_IDENTITY`].
1834        /// 2. Certified key MUST be of [`CertifiedKey::Ed25519`].
1835        /// 3. Certified key MUST be equal to `id_ed25519`.
1836        /// 4. MUST have a valid signature.
1837        pub fn verify(
1838            ntor_ed25519: ed25519::Ed25519Identity,
1839            id_ed25519: ed25519::Ed25519Identity,
1840            cert: KeyUnknownCert,
1841        ) -> StdResult<TimeRangeBound<Self>, VerifyFailed> {
1842            Ok(
1843                // .verify_inner() ensures 1-3.
1844                Self::verify_inner(ntor_ed25519, id_ed25519, cert)?
1845                    .0
1846                    // 4. MUST have a valid signature.
1847                    .check_signature()?,
1848            )
1849        }
1850
1851        /// Creates a new signed [`Ed25519NtorCrossCert`].
1852        pub fn new_signed(
1853            ntor_ed25519: &ed25519::ExpandedKeypair,
1854            id_ed25519: ed25519::Ed25519Identity,
1855            expiry: SystemTime,
1856        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
1857            let cert = Ed25519Cert::builder()
1858                .expiration(expiry)
1859                .cert_type(CertType::NTOR_CC_IDENTITY)
1860                .cert_key(id_ed25519.into())
1861                .encode_and_sign(ntor_ed25519)
1862                .map_err(into_internal!("failed to encode and sign ntor cert"))?;
1863
1864            let cert =
1865                Ed25519Cert::decode(&cert).map_err(into_internal!("decode just encoded cert"))?;
1866
1867            Ok(EmbeddedCert::new(
1868                Self {
1869                    _promise_we_verified: (),
1870                },
1871                cert,
1872            ))
1873        }
1874
1875        /// Verifies the validity of a [`KeyUnknownCert`] believed to be a
1876        /// [`CertType::NTOR_CC_IDENTITY`].
1877        ///
1878        /// This function serves as glue between the legacy parser and
1879        /// [`Self::verify()`].
1880        ///
1881        /// # Requirements
1882        ///
1883        /// 1. MUST be of [`CertType::NTOR_CC_IDENTITY`].
1884        /// 2. Certified key MUST be of [`CertifiedKey::Ed25519`].
1885        /// 3. Certified key MUST be equal to `id_ed25519`.
1886        ///
1887        /// # Return Type
1888        ///
1889        /// Actual signature and time validation is done by the caller, hence
1890        /// why it returns a gated type as the first element of the tuple.
1891        /// The other elements constitute the inner signature plus the
1892        /// SystemTime denoting the expiry.  This is required for integration
1893        /// with legacy parser in order to enable pushing it to the verification
1894        /// batch, as the [`tor_checkable`] primitives do not provide access
1895        /// to the inner signatures/expiries and also do not support operations
1896        /// like cloning due to being dyn.
1897        pub(crate) fn verify_inner(
1898            ntor_ed25519: ed25519::Ed25519Identity,
1899            id_ed25519: ed25519::Ed25519Identity,
1900            cert: KeyUnknownCert,
1901        ) -> StdResult<
1902            (
1903                SignatureGated<TimeRangeBound<Self>>,
1904                ValidatableEd25519Signature,
1905                SystemTime,
1906            ),
1907            VerifyFailed,
1908        > {
1909            // 1. MUST be of [`CertType::NTOR_CC_IDENTITY`].
1910            if cert.peek_cert_type() != CertType::NTOR_CC_IDENTITY {
1911                return Err(ErrorProblem::ObjectInvalidData.into());
1912            }
1913
1914            // 2. Certified key MUST be of [`CertifiedKey::Ed25519`].
1915            // 3. Certified key MUST be equal to `id_ed25519`.
1916            if cert.peek_subject_key() != &CertifiedKey::Ed25519(id_ed25519) {
1917                return Err(VerifyFailed::VerifyFailed);
1918            }
1919
1920            // Fish out the signature from the certificate and verify it later.
1921            //
1922            // It may fail if ntor_ed25519 is not a valid mapping to a public
1923            // key.  This is okay.  The .should_be_signed_with() call is
1924            // tor_cert boilerplate and only required to obtain an
1925            // UncheckedCert, as ntor cross-certificates do not contain the
1926            // signed-with extension.
1927            let (cert, sig) = cert
1928                .should_be_signed_with(&ntor_ed25519)?
1929                .dangerously_split()?;
1930
1931            // Fish out the expiration date from the certificate.
1932            //
1933            // Important: We must not set SystemTime::UNIX_EPOCH as the lower
1934            // bound, because with TimeRangeBound, a lower-bound of zero is not
1935            // equal to an absent lower bound!
1936            let cert = cert.dangerously_assume_timely();
1937            let expiration = ..cert.expiry();
1938
1939            Ok((
1940                SignatureGated::new(
1941                    TimeRangeBound::new(
1942                        Self {
1943                            _promise_we_verified: (),
1944                        },
1945                        expiration,
1946                    ),
1947                    vec![Box::new(sig.clone())],
1948                ),
1949                sig,
1950                expiration.end,
1951            ))
1952        }
1953
1954        /// Internal function for creating an unverified instance.
1955        ///
1956        /// This is only intended for testing and legacy parser compatibility
1957        /// purposes.
1958        pub(crate) fn dangerous_new_unverified() -> Self {
1959            Self {
1960                _promise_we_verified: (),
1961            }
1962        }
1963    }
1964}
1965
1966/// Digest identifiers, and digests in the form `ALGORITHM=BASE64U`
1967///
1968/// As found in a vote's `m` line.
1969// TODO Use FixedB64 here.
1970mod identified_digest {
1971    use super::*;
1972
1973    define_derive_deftly! {
1974        /// impl `FromStr` and `Display` for an enum with unit variants but also "unknown"
1975        ///
1976        /// Expected input: an enum whose variants are either
1977        ///  * unit variants, perhaps with `#[deftly(string_repr = "string")]`
1978        ///  * singleton tuple variant, containing `String` (or near equivalent)
1979        ///
1980        /// If `#[deftly(string_repro)]` is not specified,
1981        /// the default is snake case of the variant name.
1982        //
1983        // This macro may seem overkill, but open-coding these impls gives opportunities
1984        // for mismatches between FromStr, Display, and the variant name.
1985        //
1986        // TODO consider putting this in tor-basic-utils (maybe with a better name),
1987        // or possibly asking if derive_more want their FromStr to have this.
1988        StringReprUnitsOrUnknown for enum, expect items, beta_deftly:
1989
1990        ${define STRING_REPR {
1991            ${vmeta(string_repr)
1992              as str,
1993              default { ${concat ${snake_case $vname}} }
1994            }
1995        }}
1996
1997        impl FromStr for $ttype {
1998            type Err = Void;
1999            fn from_str(s: &str) -> Result<Self, Void> {
2000                $(
2001                    ${when v_is_unit}
2002                    if s == $STRING_REPR {
2003                        return Ok($vtype)
2004                    }
2005                )
2006                $(
2007                    ${when not(v_is_unit)} // anything else had better be Unknown
2008                    // not using `return ..;` makes this a syntax error if there are several.
2009                    Ok($vtype { 0: s.into() })
2010                )
2011            }
2012        }
2013        impl AsRef<str> for $ttype {
2014            fn as_ref(&self) -> &str {
2015                match self {
2016                    $(
2017                        ${when v_is_unit}
2018                        $vtype => $STRING_REPR,
2019                    )
2020                    $(
2021                        ${when not(v_is_unit)}
2022                        $vpat => f_0,
2023                    )
2024                }
2025            }
2026        }
2027        impl Display for $ttype {
2028            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2029                let s: &str = self.as_ref();
2030                Display::fmt(s, f)
2031            }
2032        }
2033    }
2034
2035    /// The name of a digest algorithm.
2036    ///
2037    /// Can represent an unrecognised algorithm, so it's parsed and reproduced.
2038    #[derive(Debug, Clone, Eq, PartialEq, Hash, Deftly)]
2039    #[derive_deftly(StringReprUnitsOrUnknown)]
2040    #[non_exhaustive]
2041    pub enum DigestName {
2042        /// SHA-256
2043        Sha256,
2044        /// Unknown
2045        Unknown(String),
2046    }
2047
2048    /// A single digest made with a nominated digest algorithm, `ALGORITHM=DIGEST`
2049    #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, derive_more::Display)]
2050    #[display("{alg}={value}")]
2051    #[non_exhaustive]
2052    pub struct IdentifiedDigest {
2053        /// The algorithm name.
2054        alg: DigestName,
2055
2056        /// The digest value.
2057        ///
2058        /// Invariant: length is correct for `alg`, assuming `alg` is known.
2059        value: B64,
2060    }
2061
2062    impl NormalItemArgument for DigestName {}
2063    impl NormalItemArgument for IdentifiedDigest {}
2064
2065    /// Invalid syntax parsing an `IdentifiedDigest`
2066    #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, thiserror::Error)]
2067    #[error("invalid syntax, expected ALGORITHM=DIGEST: {0}")]
2068    pub struct IdentifiedDigestParseError(String);
2069
2070    impl Ord for DigestName {
2071        fn cmp(&self, other: &Self) -> Ordering {
2072            self.as_ref().cmp(other.as_ref())
2073        }
2074    }
2075    impl PartialOrd for DigestName {
2076        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2077            Some(self.cmp(other))
2078        }
2079    }
2080
2081    impl FromStr for IdentifiedDigest {
2082        type Err = IdentifiedDigestParseError;
2083
2084        fn from_str(s: &str) -> Result<Self, Self::Err> {
2085            (|| {
2086                let (alg, value) = s.split_once('=').ok_or("missing equals sign")?;
2087
2088                let alg = alg.parse().void_unwrap();
2089                let value = value
2090                    .parse::<B64>()
2091                    .map_err(|e| format!("bad value: {}", e.report()))?;
2092
2093                if let Some(exp_len) = (|| {
2094                    Some({
2095                        use DigestName::*;
2096                        match alg {
2097                            Sha256 => 32,
2098                            Unknown(_) => None?,
2099                        }
2100                    })
2101                })() {
2102                    let val_len = value.as_bytes().len();
2103                    if val_len != exp_len {
2104                        return Err(format!("got {val_len} bytes, expected {exp_len}"));
2105                    }
2106                }
2107
2108                Ok(IdentifiedDigest { alg, value })
2109            })()
2110            .map_err(IdentifiedDigestParseError)
2111        }
2112    }
2113}
2114
2115/// Types for decoding RSA fingerprints
2116mod fingerprint {
2117    use super::*;
2118    use crate::parse2::{ArgumentError, ArgumentStream, ItemArgumentParseable};
2119    use crate::{Error, NetdocErrorKind as EK, Pos, Result};
2120    use base64ct::{Base64Unpadded, Encoding as _};
2121    use itertools::Itertools;
2122    use tor_llcrypto::pk::rsa::RsaIdentity;
2123
2124    /// A hex-encoded RSA key identity (fingerprint) with spaces in it.
2125    ///
2126    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html?highlight=fingerprint#item:fingerprint>
2127    ///
2128    /// Netdoc parsing adapter for [`RsaIdentity`]
2129    #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
2130    #[derive_deftly(Transparent)]
2131    #[allow(clippy::exhaustive_structs)]
2132    pub struct SpFingerprint(pub RsaIdentity);
2133
2134    /// A hex-encoded fingerprint with no spaces.
2135    ///
2136    /// Netdoc parsing adapter for [`RsaIdentity`]
2137    #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
2138    #[derive_deftly(Transparent)]
2139    #[allow(clippy::exhaustive_structs)]
2140    pub struct Fingerprint(pub RsaIdentity);
2141
2142    /// A base64-encoded fingerprint (unpadded)
2143    ///
2144    /// Netdoc parsing adapter for [`RsaIdentity`]
2145    #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
2146    #[derive_deftly(Transparent)]
2147    #[allow(clippy::exhaustive_structs)]
2148    pub struct Base64Fingerprint(pub RsaIdentity);
2149
2150    /// A "long identity" in the format used for Family members.
2151    ///
2152    /// Netdoc parsing adapter for [`RsaIdentity`]
2153    #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
2154    #[derive_deftly(Transparent)]
2155    #[allow(clippy::exhaustive_structs)]
2156    pub(crate) struct LongIdent(pub RsaIdentity);
2157
2158    /// Helper: parse an identity from a hexadecimal string
2159    fn parse_hex_ident(s: &str) -> Result<RsaIdentity> {
2160        RsaIdentity::from_hex(s).ok_or_else(|| {
2161            EK::BadArgument
2162                .at_pos(Pos::at(s))
2163                .with_msg("wrong length on fingerprint")
2164        })
2165    }
2166
2167    impl FromStr for SpFingerprint {
2168        type Err = Error;
2169        fn from_str(s: &str) -> Result<SpFingerprint> {
2170            let ident = parse_hex_ident(&s.replace(' ', "")).map_err(|e| e.at_pos(Pos::at(s)))?;
2171            Ok(SpFingerprint(ident))
2172        }
2173    }
2174
2175    impl ItemArgumentParseable for SpFingerprint {
2176        fn from_args<'s>(
2177            args: &mut ArgumentStream<'s>,
2178        ) -> std::result::Result<Self, ArgumentError> {
2179            // Take the first 10 arguments because an SpFingerprint consists of
2180            // 10 x 4 = 40 characters.
2181            let fp = args.take(10).collect::<Vec<_>>();
2182
2183            // Less than 10 means missing arguments.
2184            if fp.len() < 10 {
2185                return Err(ArgumentError::Missing);
2186            }
2187
2188            // More than 10 should be impossible due to .take(10).
2189            debug_assert_eq!(fp.len(), 10);
2190
2191            // All arguments must be 4 characters long.
2192            if fp.iter().any(|arg| arg.len() != 4) {
2193                return Err(ArgumentError::Invalid);
2194            }
2195
2196            // Convert it to a string without spaces, RsaIdentity::from_hex will
2197            // verify the rest.
2198            Ok(Self(
2199                RsaIdentity::from_hex(fp.join("").as_str()).ok_or(ArgumentError::Invalid)?,
2200            ))
2201        }
2202    }
2203
2204    impl encode::ItemArgument for SpFingerprint {
2205        fn write_arg_onto(&self, out: &mut ItemEncoder<'_>) -> StdResult<(), Bug> {
2206            let res = self
2207                .0
2208                .to_bytes()
2209                .chunks(2)
2210                .map(|b| format!("{:02X}{:02X}", b[0], b[1]))
2211                .join(" ");
2212            debug_assert_eq!(res.len(), 4 * 10 + 9);
2213            out.args_raw_string(&res);
2214            Ok(())
2215        }
2216    }
2217
2218    impl FromStr for Base64Fingerprint {
2219        type Err = Error;
2220        fn from_str(s: &str) -> Result<Base64Fingerprint> {
2221            let b = s.parse::<super::B64>()?;
2222            let ident = RsaIdentity::from_bytes(b.as_bytes()).ok_or_else(|| {
2223                EK::BadArgument
2224                    .at_pos(Pos::at(s))
2225                    .with_msg("Wrong identity length")
2226            })?;
2227            Ok(Base64Fingerprint(ident))
2228        }
2229    }
2230
2231    impl Display for Base64Fingerprint {
2232        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2233            Display::fmt(&Base64Unpadded::encode_string(self.as_bytes()), f)
2234        }
2235    }
2236
2237    impl FromStr for Fingerprint {
2238        type Err = Error;
2239        fn from_str(s: &str) -> Result<Fingerprint> {
2240            let ident = parse_hex_ident(s).map_err(|e| e.at_pos(Pos::at(s)))?;
2241            Ok(Fingerprint(ident))
2242        }
2243    }
2244
2245    impl Display for Fingerprint {
2246        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2247            Display::fmt(&hex::encode_upper(self.as_bytes()), f)
2248        }
2249    }
2250
2251    impl FromStr for LongIdent {
2252        type Err = Error;
2253        fn from_str(mut s: &str) -> Result<LongIdent> {
2254            s = s.strip_prefix('$').unwrap_or(s);
2255            // Strip at '=' or '~' if found.
2256            s = s.split_once(['=', '~']).map(|(a, _)| a).unwrap_or(s);
2257            let ident = parse_hex_ident(s)?;
2258            Ok(LongIdent(ident))
2259        }
2260    }
2261
2262    impl Display for LongIdent {
2263        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2264            write!(f, "${}", self.0.as_hex_upper())
2265        }
2266    }
2267
2268    impl crate::NormalItemArgument for Fingerprint {}
2269    impl crate::NormalItemArgument for Base64Fingerprint {}
2270    impl crate::NormalItemArgument for LongIdent {}
2271}
2272
2273/// A type for relay nicknames
2274mod nickname {
2275    use super::*;
2276    use tinystr::TinyAsciiStr;
2277
2278    /// This is a strange limit, but it comes from Tor.
2279    const MAX_NICKNAME_LEN: usize = 19;
2280
2281    /// The nickname for a Tor relay.
2282    ///
2283    /// These nicknames are legacy mechanism that's occasionally useful in
2284    /// debugging. They should *never* be used to uniquely identify relays;
2285    /// nothing prevents two relays from having the same nickname.
2286    ///
2287    /// Nicknames are required to be ASCII, alphanumeric, and between 1 and 19
2288    /// characters inclusive.
2289    #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
2290    pub struct Nickname(tinystr::TinyAsciiStr<MAX_NICKNAME_LEN>);
2291
2292    /// Invalid nickname
2293    #[derive(Clone, Debug, thiserror::Error)]
2294    #[error("invalid nickname")]
2295    #[non_exhaustive]
2296    pub struct InvalidNickname {}
2297
2298    impl Nickname {
2299        /// Return a view of this nickname as a string slice.
2300        pub fn as_str(&self) -> &str {
2301            self.0.as_str()
2302        }
2303    }
2304
2305    impl Display for Nickname {
2306        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2307            self.as_str().fmt(f)
2308        }
2309    }
2310
2311    impl FromStr for Nickname {
2312        type Err = InvalidNickname;
2313
2314        fn from_str(s: &str) -> Result<Self, InvalidNickname> {
2315            let tiny = TinyAsciiStr::from_str(s).map_err(|_| InvalidNickname {})?;
2316
2317            if tiny.is_ascii_alphanumeric() && !tiny.is_empty() {
2318                Ok(Nickname(tiny))
2319            } else {
2320                Err(InvalidNickname {})
2321            }
2322        }
2323    }
2324
2325    impl AsRef<str> for Nickname {
2326        fn as_ref(&self) -> &str {
2327            self.as_str()
2328        }
2329    }
2330
2331    impl crate::NormalItemArgument for Nickname {}
2332}
2333
2334/// Hostnames etc.
2335//
2336// TODO maybe move all this to tor-basic-utils
2337mod hostname {
2338    use super::*;
2339    use std::net::IpAddr;
2340
2341    /// Internet hostname
2342    ///
2343    /// Valid according to Internet RFC1123,
2344    /// with the additional restriction that there must be at least one letter.
2345    /// (That means that anything accepted as a `Hostname`
2346    /// won't be accepted as an address even by very relaxed IPv4 address parsers.
2347    /// We presume that no TLD will ever exist that is entirely decimal digits.)
2348    ///
2349    /// Preserves case.
2350    ///
2351    /// Reserved hostname such as `example.come`, `tor.invalid` and `localhost`
2352    /// are accepted.
2353    ///
2354    /// # Comparisons; `PartialEq`, `Eq`
2355    ///
2356    /// The `PartialEq` and `Eq` implementations are case sensitive,
2357    /// even though internet hostnames are not case-sensitive.
2358    ///
2359    /// Comparing hostnames for identical apparent meaning is complicated.
2360    /// If you need to do that, you (may) need to engage with punycode (IDN),
2361    /// as well as arranging for a case-insensitive comparison.
2362    ///
2363    /// And of course, hostnames reference to the DNS.
2364    /// A single host may have multiple names and it may change its address.
2365    #[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] //
2366    #[derive(derive_more::Into, derive_more::Deref, derive_more::AsRef, derive_more::Display)]
2367    pub struct Hostname(String);
2368
2369    /// Hostname, or IP address (v4 or v6)
2370    ///
2371    /// Preserves hostname case.  See [`Hostname`].
2372    ///
2373    /// Reserved hostnames and addresses (eg `0.0.0.0` or `tor.invalid`) are accepted.
2374    ///
2375    /// IPv6 addresses are represented *without* surrounding `[ ]`.
2376    ///
2377    /// Therefore, you cannot make this into a host-and-port by appending `:port`.
2378    /// To process name-and-port is complex.  `SRV` (or `MX`) records might be involved.
2379    //
2380    // This type is called `InternetHost` to emphasise that it is primarily for
2381    // hosts on the public internet and, unlike arti-client's `Host`,
2382    // has special handling of `.onion` addresses.
2383    #[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] //
2384    #[derive(derive_more::Display)]
2385    #[allow(clippy::exhaustive_enums)]
2386    // TODO derive .as_hostname(), .as_ip_addr(), From<Hostname>, From<IpAddr>
2387    pub enum InternetHost {
2388        /// Hostname
2389        #[display("{_0}")]
2390        Name(Hostname),
2391        /// IP address (v4 or v6)
2392        #[display("{_0}")]
2393        IpAddr(IpAddr),
2394    }
2395
2396    /// Invalid hostname
2397    #[derive(Clone, Debug, thiserror::Error)]
2398    #[error("invalid hostname")]
2399    #[non_exhaustive]
2400    pub struct InvalidHostname {}
2401
2402    /// Invalid Internet hostname/address
2403    #[derive(Clone, Debug, thiserror::Error)]
2404    #[error("invalid: not a valid hostname, nor a valid IPv4 or IPv6 address")]
2405    #[non_exhaustive]
2406    pub struct InvalidInternetHost {}
2407
2408    impl Hostname {
2409        /// Obtain this hostname as a `str`
2410        pub fn as_str(&self) -> &str {
2411            &self.0
2412        }
2413    }
2414
2415    impl AsRef<str> for Hostname {
2416        fn as_ref(&self) -> &str {
2417            self.as_str()
2418        }
2419    }
2420
2421    impl TryFrom<String> for Hostname {
2422        type Error = InvalidHostname;
2423        fn try_from(s: String) -> Result<Self, InvalidHostname> {
2424            if hostname_validator::is_valid(&s) &&
2425                // Reject hostnames that consist only of decimal digits and full stops.
2426                // Some of those are accepted by some old IPv4 address parsers.
2427                // If any fool makes a TLD that is only digits, they deserve everything they get.
2428                !s.chars().all(|c| c.is_ascii_digit() || c == '.')
2429            {
2430                Ok(Hostname(s))
2431            } else {
2432                Err(InvalidHostname {})
2433            }
2434        }
2435    }
2436
2437    impl FromStr for Hostname {
2438        type Err = InvalidHostname;
2439        fn from_str(s: &str) -> Result<Self, InvalidHostname> {
2440            s.to_owned().try_into()
2441        }
2442    }
2443
2444    impl FromStr for InternetHost {
2445        type Err = InvalidInternetHost;
2446        fn from_str(s: &str) -> Result<Self, InvalidInternetHost> {
2447            if let Ok(y) = s.parse() {
2448                Ok(InternetHost::IpAddr(y))
2449            } else if let Ok(y) = s.parse() {
2450                Ok(InternetHost::Name(y))
2451            } else {
2452                // For simplicity, we  discard the errors from parsing the options
2453                // rather than trying to reproduce them.  Why something isn't a valid
2454                // address or hostname ought to be fairly obvious.
2455                Err(InvalidInternetHost {})
2456            }
2457        }
2458    }
2459
2460    impl NormalItemArgument for Hostname {}
2461    impl NormalItemArgument for InternetHost {}
2462}
2463
2464/// Contact information of the relay operator.
2465mod contact_info {
2466    use super::*;
2467
2468    /// `contact` item: contact information (eg of a relay dirauth operator)
2469    ///
2470    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:contact>
2471    ///
2472    /// Also used for authority entries in netstatus documents.
2473    #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Deftly)] //
2474    #[derive(derive_more::Into, derive_more::AsRef, derive_more::Deref, derive_more::Display)]
2475    #[derive_deftly(ItemValueEncodable)]
2476    #[non_exhaustive]
2477    pub struct ContactInfo(#[deftly(netdoc(rest))] String);
2478
2479    /// Contact information (`contact` item value) has invalid syntax
2480    #[derive(Clone, Debug, thiserror::Error)]
2481    #[error("contact information (`contact` item value) has invalid syntax")]
2482    #[non_exhaustive]
2483    pub struct InvalidContactInfo {}
2484
2485    impl FromStr for ContactInfo {
2486        type Err = InvalidContactInfo;
2487
2488        fn from_str(s: &str) -> Result<Self, InvalidContactInfo> {
2489            // TODO torspec#396 we should probably impose more restrictions
2490            // For now we forbid `\n` and initial whitespace, which is enough to ensure
2491            // that all values will roundtrip unchanged through netdoc encoding and parsing.
2492            if s.contains('\n') || s.starts_with(char::is_whitespace) {
2493                Err(InvalidContactInfo {})
2494            } else {
2495                Ok(ContactInfo(s.to_owned()))
2496            }
2497        }
2498    }
2499
2500    impl ItemValueParseable for ContactInfo {
2501        fn from_unparsed(mut item: UnparsedItem<'_>) -> Result<Self, parse2::ErrorProblem> {
2502            item.check_no_object()?;
2503            item.args_mut()
2504                .into_remaining()
2505                .parse()
2506                .map_err(|_e| item.args().handle_error("info", ArgumentError::Invalid))
2507        }
2508    }
2509}
2510
2511/// Types for boolean-like types.
2512mod boolean {
2513    use derive_deftly::Deftly;
2514    use std::{
2515        fmt::Display,
2516        ops::{Deref, DerefMut},
2517        str::FromStr,
2518    };
2519
2520    use crate::{Error, NetdocErrorKind as EK, NormalItemArgument, Pos};
2521
2522    /// A boolean that is represented by a `0` (false) or `1` (true).
2523    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Deftly)]
2524    #[derive_deftly(Transparent)]
2525    #[allow(clippy::exhaustive_structs)]
2526    pub struct NumericBoolean(pub bool);
2527
2528    impl FromStr for NumericBoolean {
2529        type Err = Error;
2530
2531        fn from_str(s: &str) -> Result<Self, Self::Err> {
2532            match s {
2533                "0" => Ok(Self(false)),
2534                "1" => Ok(Self(true)),
2535                _ => Err(EK::BadArgument
2536                    .at_pos(Pos::at(s))
2537                    .with_msg("Invalid numeric boolean")),
2538            }
2539        }
2540    }
2541
2542    impl Display for NumericBoolean {
2543        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2544            write!(f, "{}", u8::from(self.0))
2545        }
2546    }
2547
2548    impl NormalItemArgument for NumericBoolean {}
2549}
2550
2551#[cfg(test)]
2552mod test {
2553    // @@ begin test lint list maintained by maint/add_warning @@
2554    #![allow(clippy::bool_assert_comparison)]
2555    #![allow(clippy::clone_on_copy)]
2556    #![allow(clippy::dbg_macro)]
2557    #![allow(clippy::mixed_attributes_style)]
2558    #![allow(clippy::print_stderr)]
2559    #![allow(clippy::print_stdout)]
2560    #![allow(clippy::single_char_pattern)]
2561    #![allow(clippy::unwrap_used)]
2562    #![allow(clippy::unchecked_time_subtraction)]
2563    #![allow(clippy::useless_vec)]
2564    #![allow(clippy::needless_pass_by_value)]
2565    #![allow(clippy::string_slice)] // See arti#2571
2566    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
2567    use std::{
2568        fmt::Debug,
2569        time::{Duration, SystemTime},
2570    };
2571
2572    use itertools::Itertools;
2573
2574    use base64ct::Encoding;
2575    use tor_basic_utils::test_rng::testing_rng;
2576    use tor_cert::{CertType, CertifiedKey, Ed25519Cert, KeyUnknownCert};
2577    use tor_checkable::{TimeBound, timed::TimeRangeBound};
2578    use tor_llcrypto::pk::ed25519::{self, Ed25519Identity, Ed25519PublicKey, ExpandedKeypair};
2579
2580    use super::*;
2581    use crate::{
2582        Pos, Result,
2583        encode::{NetdocEncodable, encode_netdoc_unsigned},
2584        parse2::{ErrorProblem, ParseInput, VerifyFailed},
2585        types::EmbeddedCert,
2586    };
2587
2588    /// Decode s as a multi-line base64 string, ignoring ascii whitespace.
2589    fn base64_decode_ignore_ws(s: &str) -> std::result::Result<Vec<u8>, base64ct::Error> {
2590        let mut s = s.to_string();
2591        s.retain(|c| !c.is_ascii_whitespace());
2592        base64ct::Base64::decode_vec(s.as_str())
2593    }
2594
2595    #[test]
2596    fn base64() -> Result<()> {
2597        // Test parsing success:
2598        // Unpadded:
2599        assert_eq!("Mi43MTgyOA".parse::<B64>()?.as_bytes(), &b"2.71828"[..]);
2600        assert!("Mi43MTgyOA".parse::<B64>()?.check_len(7..8).is_ok());
2601        assert_eq!("Mg".parse::<B64>()?.as_bytes(), &b"2"[..]);
2602        assert!("Mg".parse::<B64>()?.check_len(1..2).is_ok());
2603        assert_eq!(
2604            "8J+NkvCfjZLwn42S8J+NkvCfjZLwn42S"
2605                .parse::<B64>()?
2606                .as_bytes(),
2607            "🍒🍒🍒🍒🍒🍒".as_bytes()
2608        );
2609        assert!(
2610            "8J+NkvCfjZLwn42S8J+NkvCfjZLwn42S"
2611                .parse::<B64>()?
2612                .check_len(24..25)
2613                .is_ok()
2614        );
2615        assert!(
2616            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz8="
2617                .parse::<B64>()?
2618                .check_len(32..33)
2619                .is_ok()
2620        );
2621        // Padded:
2622        assert_eq!("Mi43MTgyOA==".parse::<B64>()?.as_bytes(), &b"2.71828"[..]);
2623        assert!("Mi43MTgyOA==".parse::<B64>()?.check_len(7..8).is_ok());
2624        assert_eq!("Mg==".parse::<B64>()?.as_bytes(), &b"2"[..]);
2625        assert!("Mg==".parse::<B64>()?.check_len(1..2).is_ok());
2626
2627        // Test parsing failures:
2628        // Invalid character.
2629        assert!("Mi43!!!!!!".parse::<B64>().is_err());
2630        // Invalid last character.
2631        assert!("Mi".parse::<B64>().is_err());
2632        assert!(
2633            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxaaaa"
2634                .parse::<B64>()
2635                .is_err()
2636        );
2637        // Invalid length.
2638        assert!("Mi43MTgyOA".parse::<B64>()?.check_len(8..).is_err());
2639        Ok(())
2640    }
2641
2642    #[test]
2643    fn base64_lengths() -> Result<()> {
2644        assert_eq!("".parse::<B64>()?.as_bytes(), b"");
2645        assert!("=".parse::<B64>().is_err());
2646        assert!("==".parse::<B64>().is_err());
2647        assert!("B".parse::<B64>().is_err());
2648        assert!("B=".parse::<B64>().is_err());
2649        assert!("B==".parse::<B64>().is_err());
2650        assert!("Bg=".parse::<B64>().is_err());
2651        assert_eq!("Bg".parse::<B64>()?.as_bytes(), b"\x06");
2652        assert_eq!("Bg==".parse::<B64>()?.as_bytes(), b"\x06");
2653        assert_eq!("BCg".parse::<B64>()?.as_bytes(), b"\x04\x28");
2654        assert_eq!("BCg=".parse::<B64>()?.as_bytes(), b"\x04\x28");
2655        assert!("BCg==".parse::<B64>().is_err());
2656        assert_eq!("BCDE".parse::<B64>()?.as_bytes(), b"\x04\x20\xc4");
2657        assert!("BCDE=".parse::<B64>().is_err());
2658        assert!("BCDE==".parse::<B64>().is_err());
2659        Ok(())
2660    }
2661
2662    #[test]
2663    fn base64_rev() {
2664        use base64ct::{Base64, Base64Unpadded};
2665
2666        // Check that strings that we accept are precisely ones which
2667        // can be generated by either Base64 or Base64Unpadded
2668        for n in 0..=5 {
2669            for c_vec in std::iter::repeat_n("ACEQg/=".chars(), n).multi_cartesian_product() {
2670                let s: String = c_vec.into_iter().collect();
2671                #[allow(clippy::print_stderr)]
2672                let b = match s.parse::<B64>() {
2673                    Ok(b) => {
2674                        eprintln!("{:10} {:?}", s, b.as_bytes());
2675                        b
2676                    }
2677                    Err(_) => {
2678                        eprintln!("{:10} Err", s);
2679                        continue;
2680                    }
2681                };
2682                let b = b.as_bytes();
2683
2684                let ep = Base64::encode_string(b);
2685                let eu = Base64Unpadded::encode_string(b);
2686
2687                assert!(
2688                    s == ep || s == eu,
2689                    "{:?} decoded to {:?} giving neither {:?} nor {:?}",
2690                    s,
2691                    b,
2692                    ep,
2693                    eu
2694                );
2695            }
2696        }
2697    }
2698
2699    #[test]
2700    fn base16() -> anyhow::Result<()> {
2701        let chk = |s: &str, b: &[u8]| -> anyhow::Result<()> {
2702            let parsed = s.parse::<B16>()?;
2703            assert_eq!(parsed.as_bytes(), b, "{s:?}");
2704            assert_eq!(parsed.to_string(), s.to_ascii_lowercase());
2705
2706            let parsed = s.parse::<B16U>()?;
2707            assert_eq!(parsed.as_bytes(), b, "{s:?}");
2708            assert_eq!(parsed.to_string(), s.to_ascii_uppercase());
2709            Ok(())
2710        };
2711
2712        chk("332e313432", b"3.142")?;
2713        chk("332E313432", b"3.142")?;
2714        chk("332E3134", b"3.14")?;
2715
2716        assert!("332E313".parse::<B16>().is_err());
2717        assert!("332G3134".parse::<B16>().is_err());
2718        Ok(())
2719    }
2720
2721    #[test]
2722    fn curve25519() -> Result<()> {
2723        use tor_llcrypto::pk::curve25519::PublicKey;
2724        let k1 = "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz8=";
2725        let k2 = hex::decode("a69c2d8475d6f245c3d1ff5f13b50f62c38002ee2e8f9391c12a2608cc4a933f")
2726            .unwrap();
2727        let k2: &[u8; 32] = &k2[..].try_into().unwrap();
2728
2729        let k1: PublicKey = k1.parse::<Curve25519Public>()?.into();
2730        assert_eq!(k1, (*k2).into());
2731
2732        assert!(
2733            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz"
2734                .parse::<Curve25519Public>()
2735                .is_err()
2736        );
2737        assert!(
2738            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORSomCMxKkz"
2739                .parse::<Curve25519Public>()
2740                .is_err()
2741        );
2742        assert!(
2743            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5wSomCMxKkz"
2744                .parse::<Curve25519Public>()
2745                .is_err()
2746        );
2747        assert!(
2748            "ppwthHXW8kXD0f9fE7UPYsOAAu4ORwSomCMxKkz"
2749                .parse::<Curve25519Public>()
2750                .is_err()
2751        );
2752
2753        Ok(())
2754    }
2755
2756    #[test]
2757    fn ed25519() -> Result<()> {
2758        use tor_llcrypto::pk::ed25519::Ed25519Identity;
2759        let k1 = "WVIPQ8oArAqLY4XzkcpIOI6U8KsUJHBQhG8SC57qru0";
2760        let k2 = hex::decode("59520f43ca00ac0a8b6385f391ca48388e94f0ab14247050846f120b9eeaaeed")
2761            .unwrap();
2762
2763        let k1: Ed25519Identity = k1.parse::<Ed25519Public>()?.into();
2764        assert_eq!(k1, Ed25519Identity::from_bytes(&k2).unwrap());
2765
2766        assert!(
2767            "WVIPQ8oArAqLY4Xzk0!!!!8KsUJHBQhG8SC57qru"
2768                .parse::<Ed25519Public>()
2769                .is_err()
2770        );
2771        assert!(
2772            "WVIPQ8oArAqLY4XzkcpIU8KsUJHBQhG8SC57qru"
2773                .parse::<Ed25519Public>()
2774                .is_err()
2775        );
2776        assert!(
2777            "WVIPQ8oArAqLY4XzkcpIU8KsUJHBQhG8SC57qr"
2778                .parse::<Ed25519Public>()
2779                .is_err()
2780        );
2781        // right length, bad key:
2782        assert!(
2783            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxaaaa"
2784                .parse::<Curve25519Public>()
2785                .is_err()
2786        );
2787        Ok(())
2788    }
2789
2790    #[test]
2791    fn time() -> Result<()> {
2792        use humantime::parse_rfc3339;
2793        use std::time::SystemTime;
2794
2795        let t = "2020-09-29 13:36:33".parse::<Iso8601TimeSp>()?;
2796        let t: SystemTime = t.into();
2797        assert_eq!(t, parse_rfc3339("2020-09-29T13:36:33Z").unwrap());
2798
2799        assert!("2020-FF-29 13:36:33".parse::<Iso8601TimeSp>().is_err());
2800        assert!("2020-09-29Q13:99:33".parse::<Iso8601TimeSp>().is_err());
2801        assert!("2020-09-29".parse::<Iso8601TimeSp>().is_err());
2802        assert!("too bad, waluigi time".parse::<Iso8601TimeSp>().is_err());
2803
2804        assert_eq!(
2805            "2020-09-29 13:36:33",
2806            "2020-09-29 13:36:33".parse::<Iso8601TimeSp>()?.to_string()
2807        );
2808
2809        let t = "2020-09-29T13:36:33".parse::<Iso8601TimeNoSp>()?;
2810        let t: SystemTime = t.into();
2811        assert_eq!(t, parse_rfc3339("2020-09-29T13:36:33Z").unwrap());
2812
2813        assert!("2020-09-29 13:36:33".parse::<Iso8601TimeNoSp>().is_err());
2814        assert!("2020-09-29Q13:99:33".parse::<Iso8601TimeNoSp>().is_err());
2815        assert!("2020-09-29".parse::<Iso8601TimeNoSp>().is_err());
2816        assert!("too bad, waluigi time".parse::<Iso8601TimeNoSp>().is_err());
2817
2818        assert_eq!(
2819            "2020-09-29T13:36:33",
2820            "2020-09-29T13:36:33"
2821                .parse::<Iso8601TimeNoSp>()?
2822                .to_string()
2823        );
2824
2825        Ok(())
2826    }
2827
2828    #[test]
2829    fn rsa_public_key() {
2830        // Taken from a chutney network.
2831        let key_b64 = r#"
2832        MIIBigKCAYEAsDkzTcKS4kAF56R2ijb9qCek53tKC1EwMdpWMk58bB28fY6kHc55
2833        E7n1hB+LC5neZlx88GKuZ9k8P3g0MlO5ejalcfBdIIm28Nz86JXf/L23YnEpxnG/
2834        IpxZEcmx/EYN+vwp72W3DGuzyntaoaut6lGJk+O/aRCLLcTm4MNznvN1ackK2H6b
2835        Xm2ejRwtVRLoPKODJiPGl43snCfXXWsMH3IALFOgm0szPLv2fAJzBI8VWrUN81M/
2836        lgwJhG6+xbr1CkrXI5fKs/TNr0B0ydC9BIZplmPrnXaeNklnw1cqUJ1oxDSgBrvx
2837        rpDo7paObjSPV26opa68QKGa7Gu2MZQC3RzViNCbawka/108g6hSUkoM+Om2oivr
2838        DvtMOs10MjsfibEBVnwEhqnlb/gj3hJkYoGRsCwAyMIaMObHcmAevMJRWAjGCc8T
2839        GMS9dSmg1IZst+U+V2OCcIHXT6wZ1zPsBM0pYKVLCwtewaq1306k0n+ekriEo7eI
2840        FS3Dd/Dx/a6jAgMBAAE=
2841        "#;
2842        let key_bytes = base64_decode_ignore_ws(key_b64).unwrap();
2843        let rsa = RsaPublicParse1Helper::from_vec(key_bytes, Pos::None).unwrap();
2844
2845        let bits = tor_llcrypto::pk::rsa::PublicKey::from(rsa.clone()).bits();
2846        assert_eq!(bits, 3072);
2847
2848        // tests on a valid key
2849        assert!(rsa.clone().check_exponent(65537).is_ok());
2850        assert!(rsa.clone().check_exponent(1337).is_err());
2851        assert!(rsa.clone().check_len_eq(3072).is_ok());
2852        assert!(rsa.clone().check_len(1024..=4096).is_ok());
2853        assert!(rsa.clone().check_len(1024..=1024).is_err());
2854        assert!(rsa.check_len(4096..).is_err());
2855
2856        // A string of bytes that is not an RSA key.
2857        let failure = RsaPublicParse1Helper::from_vec(vec![1, 2, 3], Pos::None);
2858        assert!(failure.is_err());
2859    }
2860
2861    #[test]
2862    fn ed_cert() {
2863        use tor_llcrypto::pk::ed25519::Ed25519Identity;
2864
2865        // From a chutney network.
2866        let cert_b64 = r#"
2867        AQQABwRNAR6m3kq5h8i3wwac+Ti293opoOP8RKGP9MT0WD4Bbz7YAQAgBACGCdys
2868        G7AwsoYMIKenDN6In6ReiGF8jaYoGqmWKDVBdGGMDIZyNIq+VdhgtAB1EyNFHJU1
2869        jGM0ir9dackL+PIsHbzJH8s/P/8RfUsKIL6/ZHbn3nKMxLH/8kjtxp5ScAA=
2870        "#;
2871        let cert_bytes = base64_decode_ignore_ws(cert_b64).unwrap();
2872        // From the cert above.
2873        let right_subject_key: Ed25519Identity = "HqbeSrmHyLfDBpz5OLb3eimg4/xEoY/0xPRYPgFvPtg"
2874            .parse::<Ed25519Public>()
2875            .unwrap()
2876            .into();
2877        // From `ed25519()` test above.
2878        let wrong_subject_key: Ed25519Identity = "WVIPQ8oArAqLY4XzkcpIOI6U8KsUJHBQhG8SC57qru0"
2879            .parse::<Ed25519Public>()
2880            .unwrap()
2881            .into();
2882
2883        // decode and check correct type and key
2884        let cert = UnvalidatedEdCert::from_vec(cert_bytes, Pos::None)
2885            .unwrap()
2886            .check_cert_type(tor_cert::CertType::IDENTITY_V_SIGNING)
2887            .unwrap()
2888            .check_subject_key_is(&right_subject_key)
2889            .unwrap();
2890        // check wrong type.
2891        assert!(
2892            cert.clone()
2893                .check_cert_type(tor_cert::CertType::RSA_ID_X509)
2894                .is_err()
2895        );
2896        // check wrong key.
2897        assert!(cert.check_subject_key_is(&wrong_subject_key).is_err());
2898
2899        // Try an invalid object that isn't a certificate.
2900        let failure = UnvalidatedEdCert::from_vec(vec![1, 2, 3], Pos::None);
2901        assert!(failure.is_err());
2902    }
2903
2904    #[test]
2905    fn fingerprint() -> Result<()> {
2906        use tor_llcrypto::pk::rsa::RsaIdentity;
2907        let fp1 = "7467 A97D 19CD 2B4F 2BC0 388A A99C 5E67 710F 847E";
2908        let fp2 = "7467A97D19CD2B4F2BC0388AA99C5E67710F847E";
2909        let fp3 = "$7467A97D19CD2B4F2BC0388AA99C5E67710F847E";
2910        let fp4 = "$7467A97D19CD2B4F2BC0388AA99C5E67710F847E=fred";
2911
2912        let k = hex::decode(fp2).unwrap();
2913        let k = RsaIdentity::from_bytes(&k[..]).unwrap();
2914
2915        assert_eq!(RsaIdentity::from(fp1.parse::<SpFingerprint>()?), k);
2916        assert_eq!(RsaIdentity::from(fp2.parse::<SpFingerprint>()?), k);
2917        assert!(fp3.parse::<SpFingerprint>().is_err());
2918        assert!(fp4.parse::<SpFingerprint>().is_err());
2919
2920        assert!(fp1.parse::<Fingerprint>().is_err());
2921        assert_eq!(RsaIdentity::from(fp2.parse::<Fingerprint>()?), k);
2922        assert!(fp3.parse::<Fingerprint>().is_err());
2923        assert!(fp4.parse::<Fingerprint>().is_err());
2924        assert_eq!(Fingerprint(k).to_string(), fp2);
2925
2926        assert!(fp1.parse::<LongIdent>().is_err());
2927        assert_eq!(RsaIdentity::from(fp2.parse::<LongIdent>()?), k);
2928        assert_eq!(RsaIdentity::from(fp3.parse::<LongIdent>()?), k);
2929        assert_eq!(RsaIdentity::from(fp4.parse::<LongIdent>()?), k);
2930
2931        assert!("xxxx".parse::<Fingerprint>().is_err());
2932        assert!("ffffffffff".parse::<Fingerprint>().is_err());
2933
2934        let fp_b64 = "dGepfRnNK08rwDiKqZxeZ3EPhH4";
2935        assert_eq!(RsaIdentity::from(fp_b64.parse::<Base64Fingerprint>()?), k);
2936        assert_eq!(Base64Fingerprint(k).to_string(), fp_b64);
2937
2938        Ok(())
2939    }
2940
2941    #[test]
2942    fn nickname() -> anyhow::Result<()> {
2943        let n: Nickname = "Foo".parse()?;
2944        assert_eq!(n.as_str(), "Foo");
2945        assert_eq!(n.to_string(), "Foo");
2946
2947        let word = "Untr1gonometr1cally";
2948        assert_eq!(word.len(), 19);
2949        let long: Nickname = word.parse()?;
2950        assert_eq!(long.as_str(), word);
2951
2952        let too_long = "abcdefghijklmnopqrstuvwxyz";
2953        let not_ascii = "Eyjafjallajökull";
2954        let too_short = "";
2955        let other_invalid = "contains space";
2956        assert!(not_ascii.len() <= 19);
2957        assert!(too_long.parse::<Nickname>().is_err());
2958        assert!(not_ascii.parse::<Nickname>().is_err());
2959        assert!(too_short.parse::<Nickname>().is_err());
2960        assert!(other_invalid.parse::<Nickname>().is_err());
2961
2962        Ok(())
2963    }
2964
2965    /// Test for both `Hostname` and `InternetHost`
2966    #[test]
2967    fn hostname() {
2968        use std::net::IpAddr;
2969
2970        // Test a string that we should treat as a valid hostname.
2971        let chk_name = |s: &str| {
2972            let n: Hostname = s.parse().expect(s);
2973            assert_eq!(n.as_str(), s);
2974            assert_eq!(n.to_string(), s);
2975            assert_eq!(s.parse::<InternetHost>().expect(s), InternetHost::Name(n));
2976        };
2977
2978        // Test a string that looks like it could be an address or a hostname.
2979        // We parse those as addresses.
2980        let chk_either = |s: &str| {
2981            let h: InternetHost = s.parse().expect(s);
2982            let a: IpAddr = s.parse().expect(s);
2983            assert_eq!(h, InternetHost::IpAddr(a), "{s:?}");
2984            assert_eq!(h.to_string(), a.to_string(), "{s:?}");
2985        };
2986
2987        // Test a string that's an address, and isn't a valid hostname.
2988        let chk_addr = |s: &str| {
2989            let _: InvalidHostname = s.parse::<Hostname>().expect_err(s);
2990            chk_either(s);
2991        };
2992
2993        // Test a string that we should reject.
2994        let chk_bad = |s: &str| {
2995            let _: InvalidHostname = s.parse::<Hostname>().expect_err(s);
2996            let _: InvalidInternetHost = s.parse::<InternetHost>().expect_err(s);
2997        };
2998
2999        chk_name("foo.bar");
3000        chk_name("localhost");
3001        chk_name("tor.invalid");
3002        chk_name("example.com");
3003
3004        // Unarguably invalid.
3005        chk_bad("");
3006        chk_bad("foo bar");
3007        chk_bad("foo..bar");
3008        chk_bad("foo.-bar");
3009        chk_bad(" foo.bar ");
3010        chk_bad("[::1]");
3011
3012        // Strings that some IP address parsers accept as addresses,
3013        // but which are also valid hostnames according to RFC1123.
3014        //
3015        // We reject them rather than processing of them as hostnames,
3016        // exposing downstream software to possible strangeness.
3017        chk_bad("1");
3018        chk_bad("127.0.0.023");
3019
3020        // No-one thinks this is a valid IP address but we reject it as a hostname too,
3021        // even though it's syntactically legal per RFC1123, because it's quite bad.
3022        chk_bad("1.2.3.4.5");
3023
3024        chk_either("0.0.0.0");
3025        chk_either("127.0.0.1");
3026        chk_addr("::");
3027        chk_addr("::1");
3028        chk_addr("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
3029        chk_addr("::ffff:192.0.2.3"); // IPv6-mapped IPv4 address
3030    }
3031
3032    #[test]
3033    fn contact_info() -> anyhow::Result<()> {
3034        use parse2::{ParseInput, parse_netdoc};
3035
3036        const S: &str = "some relay operator";
3037        let n: ContactInfo = S.parse()?;
3038        assert_eq!(n.as_str(), S);
3039        assert_eq!(n.to_string(), S);
3040
3041        let bad = |s: &str| {
3042            let _: InvalidContactInfo = s.parse::<ContactInfo>().unwrap_err();
3043        };
3044
3045        bad(" starts with space");
3046        bad("contains\nnewline");
3047
3048        #[derive(PartialEq, Debug, Deftly)]
3049        #[derive_deftly(NetdocParseable, NetdocEncodable)]
3050        struct TestDoc {
3051            pub intro: (),
3052            pub contact: ContactInfo,
3053        }
3054
3055        let roundtrip = |s: &str| -> anyhow::Result<()> {
3056            let doc = TestDoc {
3057                intro: (),
3058                contact: s.parse()?,
3059            };
3060            let enc = encode_netdoc_unsigned([&doc])?;
3061            let reparsed = parse_netdoc::<TestDoc>(&ParseInput::new(&enc, "<test>"))?;
3062            assert_eq!(doc, reparsed);
3063            Ok(())
3064        };
3065
3066        roundtrip("normal")?;
3067        roundtrip("trailing  white space  ")?;
3068        roundtrip("wtf is this allowed in \x03 netdocs\r")?; // TODO torspec#396
3069
3070        Ok(())
3071    }
3072
3073    /// Round trip test for [`NumericBoolean`] ensuring that `0` is false,
3074    /// `1` is true, and other things fail.
3075    #[test]
3076    fn numeric_boolean() {
3077        let chk = |s: &str| {
3078            assert_eq!(NumericBoolean::from_str(s).expect(s).to_string(), s);
3079        };
3080        chk("0");
3081        chk("1");
3082        // Testing this because it is not a u8.
3083        assert!(NumericBoolean::from_str("10000").is_err());
3084    }
3085
3086    #[test]
3087    fn f64_finite() {
3088        let normalise_string = |i: &str, o: &str| {
3089            let v: F64Finite = i.parse().expect(i);
3090            assert_eq!(v.to_string(), o, "i={i:?}");
3091        };
3092        let roundtrip_string = |s: &str| normalise_string(s, s);
3093        let roundtrip_value = |i: f64| {
3094            let v: F64Finite = i.try_into().unwrap();
3095            let s = v.to_string();
3096            let o: F64Finite = s.parse().expect(&s);
3097            assert_eq!(v, o, "{i:?} {s}");
3098            assert_eq!(v.to_bits(), o.to_bits(), "{i:?} {s}");
3099        };
3100        let error_string = |s: &str| {
3101            let _: F64FiniteParseError = s.parse::<F64Finite>().expect_err(s);
3102        };
3103
3104        roundtrip_string("0");
3105        roundtrip_string("0.5");
3106        roundtrip_string("1");
3107        roundtrip_string("42");
3108        roundtrip_string("9007199254740991"); // f64::MAX_EXACT_INTEGER (as per Rust 1.96.0)
3109        normalise_string("1e3", "1000");
3110
3111        roundtrip_value(f64::EPSILON);
3112        roundtrip_value(f64::EPSILON + 1.0);
3113        roundtrip_value(f64::MIN);
3114        roundtrip_value(f64::MIN_POSITIVE);
3115        roundtrip_value(-f64::MIN_POSITIVE);
3116        roundtrip_value(f64::MAX);
3117
3118        error_string(&f64::NAN.to_string());
3119        error_string(&f64::INFINITY.to_string());
3120        error_string("");
3121        error_string("garbage");
3122
3123        // TODO torspec#416 these ought to be more reasonable, but this is what it does now:
3124        roundtrip_string(
3125            "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014",
3126        ); // MIN_POSITIVE
3127        roundtrip_string(
3128            "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
3129        ); // MAX
3130    }
3131
3132    /// Test that ensures SpFingerprint matches the 10x4 requirement.
3133    #[test]
3134    fn sp_fingerprint() {
3135        use derive_deftly::Deftly;
3136        use tor_llcrypto::pk::rsa::RsaIdentity;
3137
3138        use crate::parse2::ErrorProblem;
3139
3140        #[derive(Deftly)]
3141        #[derive_deftly(NetdocParseable, NetdocEncodable)]
3142        struct Wrapper {
3143            #[deftly(netdoc(single_arg))]
3144            fingerprint: SpFingerprint,
3145        }
3146
3147        /// Small helper to parse an [`SpFingerprint`].
3148        ///
3149        /// In the case the parsing went successful, it also performs a
3150        /// round-trip encoding test.
3151        fn parse2(s: &str) -> std::result::Result<SpFingerprint, ErrorProblem> {
3152            use crate::parse2::{self, ParseInput};
3153
3154            let input = format!("fingerprint {s}\n");
3155            let res = parse2::parse_netdoc::<Wrapper>(&ParseInput::new(&input, ""))
3156                .map_err(|x| x.problem)?;
3157
3158            // Round-trip encoding; we only do .starts_with() because input
3159            // may contain trailing parameters which will obviously not be
3160            // encoded; trimming to remove the trailing "\n" afterwards.
3161            let mut enc = NetdocEncoder::default();
3162            res.encode_unsigned(&mut enc).unwrap();
3163            assert!(input.starts_with(enc.finish().unwrap().trim_end()));
3164
3165            Ok(res.fingerprint)
3166        }
3167
3168        // Test a valid one.
3169        assert_eq!(
3170            parse2(&vec!["ABAB"; 10].join(" ")).unwrap(),
3171            SpFingerprint(RsaIdentity::from_bytes(&[0xAB; 20]).unwrap())
3172        );
3173
3174        // Test a valid one with tail.
3175        assert_eq!(
3176            parse2(&vec!["ABAB"; 11].join(" ")).unwrap(),
3177            SpFingerprint(RsaIdentity::from_bytes(&[0xAB; 20]).unwrap())
3178        );
3179
3180        // Missing argument
3181        assert!(matches!(
3182            parse2(&vec!["ABAB"; 9].join(" ")).unwrap_err(),
3183            ErrorProblem::MissingArgument { .. }
3184        ));
3185
3186        // Invalid argument.
3187        // In this case, we have string with a total length of 40 but with
3188        // one pair having 6 characters and another one having 2 as a proof
3189        // of that.
3190        assert!(matches!(
3191            parse2("0000 000000 00 0000 0000 0000 0000 0000 0000 0000").unwrap_err(),
3192            ErrorProblem::InvalidArgument { .. }
3193        ));
3194
3195        // And of course invalid hex should fail too.
3196        assert!(matches!(
3197            parse2(&vec!["ZZZZ"; 10].join(" ")).unwrap_err(),
3198            ErrorProblem::InvalidArgument { .. }
3199        ));
3200    }
3201
3202    /// Verifies the parsing of [`ItemPresent`].
3203    #[test]
3204    fn item_present_parse2() {
3205        #[derive(Default)]
3206        struct Token;
3207
3208        #[derive(Deftly)]
3209        #[derive_deftly(NetdocParseable)]
3210        struct TestDoc {
3211            #[allow(unused)]
3212            intro: Ignored,
3213            foo: Option<ItemPresent<Token>>,
3214        }
3215
3216        // The test cases with their respective result; boolean indicating that
3217        // it was present.
3218        let tests = [
3219            // Test valid present.
3220            ("intro\nfoo\n", Ok(true)),
3221            // Test valid absent.
3222            ("intro\n", Ok(false)),
3223            // Test repeated.
3224            ("intro\nfoo\nfoo\n", Err(ErrorProblem::ItemRepeated)),
3225            // Test repeated with unknown.
3226            ("intro\nbar\nfoo\nfoo\n", Err(ErrorProblem::ItemRepeated)),
3227            // Test with argument.
3228            ("intro\nfoo bar\n", Ok(true)),
3229            // Test with two arguments.
3230            ("intro\nfoo bar baz\n", Ok(true)),
3231            // Test with object.
3232            (
3233                "intro\nfoo\n-----BEGIN RSA PUBLIC KEY-----\n-----END RSA PUBLIC KEY-----\n",
3234                Err(ErrorProblem::ObjectUnexpected),
3235            ),
3236        ];
3237
3238        for (input, expect) in tests {
3239            println!("{input:?}, {expect:?}");
3240
3241            // Convert the result by calling .is_present() and extracting EP.
3242            let got = parse2::parse_netdoc::<TestDoc>(&ParseInput::new(input, ""))
3243                .map(|x| x.foo.is_some())
3244                .map_err(|e| e.problem);
3245            assert_eq!(got, expect);
3246        }
3247    }
3248
3249    #[test]
3250    fn item_present_encode() {
3251        #[derive(Default)]
3252        struct Token;
3253
3254        #[derive(Deftly)]
3255        #[derive_deftly(NetdocEncodable)]
3256        struct TestDoc {
3257            #[allow(unused)]
3258            intro: (),
3259            foo: Option<ItemPresent<Token>>,
3260        }
3261
3262        let tests = [
3263            (Some(ItemPresent(Token)), "intro\nfoo\n"),
3264            (None, "intro\n"),
3265        ];
3266
3267        for (present, output) in tests {
3268            let re_encoded = encode_netdoc_unsigned([&TestDoc {
3269                intro: (),
3270                foo: present,
3271            }])
3272            .unwrap();
3273            assert_eq!(re_encoded, output);
3274        }
3275    }
3276
3277    /// Helper to call methods for edcerts.
3278    trait Ed25519CertTest: Sized + PartialEq + Eq + Debug {
3279        /// Creates a new instance.
3280        ///
3281        /// Used to create a struct in ad-hoc fashion for Eq comparison.
3282        fn new(
3283            signing_key: ed25519::Ed25519Identity,
3284            certified_key: ed25519::Ed25519Identity,
3285        ) -> Self;
3286
3287        /// Returns the expected [`CertType`].
3288        fn cert_type() -> CertType;
3289
3290        /// Calls .new_signed().
3291        ///
3292        /// This method is used to create an [`EmbeddedCert`] with a given
3293        /// signing key and a key that shall be certified.
3294        fn new_signed(
3295            signing_key: &ed25519::Keypair,
3296            certified_key: ed25519::Ed25519Identity,
3297            expiry: SystemTime,
3298        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug>;
3299
3300        /// Calls .verify().
3301        ///
3302        /// The method verifies a certificate given a pre-known certified key,
3303        /// the actual certificate, and a timestamp.
3304        fn verify(
3305            signing_key: Option<ed25519::Ed25519Identity>,
3306            certified_key: ed25519::Ed25519Identity,
3307            cert: KeyUnknownCert,
3308        ) -> StdResult<TimeRangeBound<Self>, VerifyFailed>;
3309    }
3310
3311    impl Ed25519CertTest for Ed25519IdentityCert {
3312        fn new(
3313            signing_key: ed25519::Ed25519Identity,
3314            certified_key: ed25519::Ed25519Identity,
3315        ) -> Self {
3316            Self {
3317                id_ed25519: signing_key,
3318                sign_ed25519: certified_key,
3319            }
3320        }
3321
3322        fn cert_type() -> CertType {
3323            CertType::IDENTITY_V_SIGNING
3324        }
3325
3326        fn new_signed(
3327            signing_key: &ed25519::Keypair,
3328            certified_key: ed25519::Ed25519Identity,
3329            expiry: SystemTime,
3330        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
3331            Self::new_signed(signing_key, certified_key, expiry)
3332        }
3333
3334        fn verify(
3335            _signing_key: Option<ed25519::Ed25519Identity>,
3336            _certified_key: ed25519::Ed25519Identity,
3337            cert: KeyUnknownCert,
3338        ) -> StdResult<TimeRangeBound<Self>, VerifyFailed> {
3339            Self::verify(cert)
3340        }
3341    }
3342
3343    impl Ed25519CertTest for Ed25519FamilyCert {
3344        fn new(
3345            signing_key: ed25519::Ed25519Identity,
3346            _certified_key: ed25519::Ed25519Identity,
3347        ) -> Self {
3348            Self {
3349                family_ed25519: signing_key,
3350            }
3351        }
3352
3353        fn cert_type() -> CertType {
3354            CertType::FAMILY_V_IDENTITY
3355        }
3356
3357        fn new_signed(
3358            signing_key: &ed25519::Keypair,
3359            certified_key: ed25519::Ed25519Identity,
3360            expiry: SystemTime,
3361        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
3362            Self::new_signed(signing_key, certified_key, expiry)
3363        }
3364
3365        fn verify(
3366            _signing_key: Option<ed25519::Ed25519Identity>,
3367            certified_key: ed25519::Ed25519Identity,
3368            cert: KeyUnknownCert,
3369        ) -> StdResult<TimeRangeBound<Self>, VerifyFailed> {
3370            Self::verify(certified_key, cert)
3371        }
3372    }
3373
3374    impl Ed25519CertTest for Ed25519NtorCrossCert {
3375        fn new(
3376            _signing_key: ed25519::Ed25519Identity,
3377            _certified_key: ed25519::Ed25519Identity,
3378        ) -> Self {
3379            Self::dangerous_new_unverified()
3380        }
3381
3382        fn cert_type() -> CertType {
3383            CertType::NTOR_CC_IDENTITY
3384        }
3385
3386        fn new_signed(
3387            signing_key: &ed25519::Keypair,
3388            certified_key: ed25519::Ed25519Identity,
3389            expiry: SystemTime,
3390        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
3391            Self::new_signed(&ExpandedKeypair::from(signing_key), certified_key, expiry)
3392        }
3393
3394        fn verify(
3395            signing_key: Option<ed25519::Ed25519Identity>,
3396            certified_key: ed25519::Ed25519Identity,
3397            cert: KeyUnknownCert,
3398        ) -> StdResult<TimeRangeBound<Self>, VerifyFailed> {
3399            Self::verify(signing_key.unwrap(), certified_key, cert)
3400        }
3401    }
3402
3403    /// Converts from [`Iso8601TimeSp`] to [`SystemTime`]
3404    fn str_to_st(s: &str) -> SystemTime {
3405        Iso8601TimeSp::from_str(s).unwrap().0
3406    }
3407
3408    /// Tests a valid ad-hoc generated certificate.
3409    fn ed25519_cert_rng<T: Ed25519CertTest>() {
3410        let mut rng = testing_rng();
3411        let signing_key = ed25519::Keypair::generate(&mut rng);
3412        let certified_key = ed25519::Keypair::generate(&mut rng);
3413        let now = str_to_st("2000-01-01 06:00:00");
3414        let expiry = str_to_st("2000-01-01 12:00:00");
3415
3416        // Test ad-hoc generation.
3417        let embedded_cert =
3418            T::new_signed(&signing_key, certified_key.public_key().into(), expiry).unwrap();
3419        assert_eq!(
3420            *embedded_cert.get().unwrap(),
3421            T::new(
3422                signing_key.public_key().into(),
3423                certified_key.public_key().into()
3424            )
3425        );
3426
3427        // Verify ad-hoc certificate generation.
3428        let unverified = embedded_cert.raw_unverified().clone();
3429        assert_eq!(T::cert_type(), unverified.peek_cert_type());
3430        match unverified.peek_subject_key() {
3431            CertifiedKey::Ed25519(x) => assert_eq!(
3432                *x,
3433                ed25519::Ed25519Identity::from(certified_key.public_key())
3434            ),
3435            _ => panic!(),
3436        }
3437
3438        // Finally, see if .verify() agrees.
3439        T::verify(
3440            Some(signing_key.public_key().into()),
3441            certified_key.public_key().into(),
3442            unverified.clone(),
3443        )
3444        .unwrap()
3445        .check_valid_at(&now)
3446        .unwrap();
3447
3448        // See if .verify() also agrees when expired but with toleration.
3449        T::verify(
3450            Some(signing_key.public_key().into()),
3451            certified_key.public_key().into(),
3452            unverified,
3453        )
3454        .unwrap()
3455        .extend_end_bound(Duration::from_secs(60 * 60))
3456        .check_valid_at(&now)
3457        .unwrap();
3458    }
3459
3460    /// Tests invalid Ed25519 certificates by violating various constraints.
3461    fn ed25519_cert_invalid<T: Ed25519CertTest + 'static>(requires_signed_with_ext: bool) {
3462        let mut rng = testing_rng();
3463        let now = str_to_st("2000-01-01 06:00:00");
3464        let expiry = str_to_st("2000-01-01 12:00:00");
3465        let signing_key = ed25519::Keypair::generate(&mut rng);
3466        let signing_pk = ed25519::Ed25519Identity::from(signing_key.public_key());
3467        let certified_key = ed25519::Keypair::generate(&mut rng);
3468        let certified_pk = ed25519::Ed25519Identity::from(certified_key.public_key());
3469
3470        let mut tests: Vec<(_, _, CertifiedKey, _, _)> = vec![
3471            // Testing a violation of the signature is hard because the encoder
3472            // refuses to emit such a thing.
3473            // ---
3474            // Violate timestamp.
3475            (
3476                T::cert_type(),
3477                // We achieve this by setting expiry to now - 1 day.
3478                now - Duration::from_secs(64 * 64 * 24),
3479                certified_pk.into(),
3480                Some(&signing_pk),
3481                &signing_key,
3482            ),
3483            // Violate cert type.
3484            (
3485                // Just picking something completely out of place here.
3486                CertType::LINK_AUTH_X509,
3487                expiry,
3488                certified_pk.into(),
3489                Some(&signing_pk),
3490                &signing_key,
3491            ),
3492            // Violate certified key type.
3493            (
3494                T::cert_type(),
3495                expiry,
3496                // Just pass a different CertifiedKey variant here.
3497                CertifiedKey::RsaSha256Digest(certified_pk.into()),
3498                Some(&signing_pk),
3499                &signing_key,
3500            ),
3501            // ---
3502            // Missing test for violating both keys MUST be valid mappings to a
3503            // [`ed25519::PublicKey`].  I was unable to find a single test
3504            // vector for this, even in curve25591-dalek. :/
3505        ];
3506
3507        // Violate absence of `signed-with-ed25519-key`.
3508        // This is not a violation in Ed25519NtorCrossCert.
3509        if requires_signed_with_ext {
3510            tests.push((
3511                T::cert_type(),
3512                expiry,
3513                certified_pk.into(),
3514                None,
3515                &signing_key,
3516            ));
3517        }
3518
3519        for (ctype, expiry, certified_key, signing_key, signing_kp) in tests {
3520            let mut builder = Ed25519Cert::builder()
3521                .cert_type(ctype)
3522                .expiration(expiry)
3523                .cert_key(certified_key.clone())
3524                .clone();
3525            if let Some(signing_key) = signing_key {
3526                builder = builder.signing_key(*signing_key).clone();
3527            }
3528            let cert = Ed25519Cert::decode(&builder.encode_and_sign(signing_kp).unwrap()).unwrap();
3529
3530            // We purposely always create an Ed25519Identity here from the bytes
3531            // in order to make it possible to test for invalid certified
3532            // key types.
3533            T::verify(
3534                signing_key.copied(),
3535                Ed25519Identity::from_bytes(certified_key.as_bytes()).unwrap(),
3536                cert,
3537            )
3538            .and_then(|expired| expired.check_valid_at(&now).map_err(|e| e.into()))
3539            .unwrap_err();
3540        }
3541    }
3542
3543    #[test]
3544    fn ed25519_cert_rng_test() {
3545        ed25519_cert_rng::<Ed25519IdentityCert>();
3546        ed25519_cert_rng::<Ed25519FamilyCert>();
3547        ed25519_cert_rng::<Ed25519NtorCrossCert>();
3548    }
3549
3550    #[test]
3551    fn ed25519_cert_invalid_test() {
3552        ed25519_cert_invalid::<Ed25519IdentityCert>(true);
3553        ed25519_cert_invalid::<Ed25519FamilyCert>(true);
3554        ed25519_cert_invalid::<Ed25519NtorCrossCert>(false);
3555    }
3556}