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//!
6//! These types shouldn't be exposed outside of the netdoc crate.
7
8pub use b16impl::*;
9pub use b64impl::*;
10pub use contact_info::*;
11pub use curve25519impl::*;
12pub use ed25519impl::*;
13pub use edcert::*;
14pub use fingerprint::*;
15pub use hostname::*;
16pub use rsa::*;
17pub use timeimpl::*;
18
19pub use nickname::{InvalidNickname, Nickname};
20
21pub use boolean::NumericBoolean;
22
23pub use fingerprint::{Base64Fingerprint, Fingerprint};
24
25pub use identified_digest::{DigestName, IdentifiedDigest};
26
27pub use ignored_impl::{
28    Ignored, IgnoredItemOrObjectValue, ItemPresent, NoMoreArguments, NotPresent,
29    NotPresentEachValue,
30};
31
32use crate::NormalItemArgument;
33use crate::encode::{
34    self,
35    ItemArgument,
36    ItemEncoder,
37    ItemObjectEncodable,
38    ItemValueEncodable,
39    // `E` for "encode`; different from `parse2::MultiplicitySelector`
40    MultiplicitySelector as EMultiplicitySelector,
41    NetdocEncoder,
42};
43use crate::parse2::{
44    self, ArgumentError, ArgumentStream, ItemArgumentParseable, ItemObjectParseable,
45    ItemValueParseable, SignatureHashInputs, SignatureItemParseable, UnparsedItem,
46    multiplicity::{
47        ArgumentSetMethods,
48        ItemSetMethods,
49        // `P2` for "parse2`; different from `encode::MultiplicitySelector`
50        MultiplicitySelector as P2MultiplicitySelector,
51        ObjectSetMethods,
52    },
53    sig_hashes::Sha1WholeKeywordLine,
54};
55
56use derive_deftly::{Deftly, define_derive_deftly, define_derive_deftly_module};
57use digest::Digest as _;
58use educe::Educe;
59use std::cmp::{self, Ordering, PartialOrd};
60use std::fmt::{self, Display};
61use std::iter;
62use std::marker::PhantomData;
63use std::ops::{Deref, DerefMut};
64use std::result::Result as StdResult;
65use std::str::FromStr;
66use subtle::{Choice, ConstantTimeEq};
67use tor_error::{Bug, ErrorReport as _, internal, into_internal};
68use void::{ResultVoidExt as _, Void};
69
70/// Describes a value that van be decoded from a bunch of bytes.
71///
72/// Used for decoding the objects between BEGIN and END tags.
73pub(crate) trait FromBytes: Sized {
74    /// Try to parse a value of this type from a byte slice
75    fn from_bytes(b: &[u8], p: crate::Pos) -> crate::Result<Self>;
76    /// Try to parse a value of this type from a vector of bytes,
77    /// and consume that value
78    fn from_vec(v: Vec<u8>, p: crate::Pos) -> crate::Result<Self> {
79        Self::from_bytes(&v[..], p)
80    }
81}
82
83define_derive_deftly_module! {
84    /// Implement conversion traits for a transparent newtype around bytes - shared code
85    ///
86    /// This is precisely `#[derive_deftly(Transparent)]`, but in the form of a deftly module,
87    /// so that other derives (eg `BytesTransparent`) can re-use it.
88    Transparent beta_deftly:
89
90    // Expands to bullet points for "generated code", except omitting
91    // `AsRef` & `AsMut` because some uses sites have additional impls of those,
92    // which are best presented together in the docs.
93  ${define TRANSPARENT_DOCS_IMPLS {
94    ///  * impls of `Deref`, `DerefMut`
95    ///  * impls of `From<field>` and "`Into`" (technically, `From<Self> for field`)
96  }}
97
98    // Expands to the implementations
99  ${define TRANSPARENT_IMPLS {
100
101  ${for fields {
102    ${loop_exactly_1 "must be applied to a single-field struct"}
103
104    impl<$tgens> From<$ftype> for $ttype {
105        fn from($fpatname: $ftype) -> $ttype {
106            $vpat
107        }
108    }
109
110    // TODO: This implementation is probably a bug, as it forbids to derive
111    // Transparent on types like `struct Foo<T>(T)`, namely `T` not being
112    // covered by something else, like `PhantomData<T>` or `Vec<T>`.
113    impl<$tgens> From<$ttype> for $ftype {
114        fn from(self_: $ttype) -> $ftype {
115            self_.$fname
116        }
117    }
118
119    impl<$tgens> Deref for $ttype {
120        type Target = $ftype;
121        fn deref(&self) -> &$ftype {
122            &self.$fname
123        }
124    }
125
126    impl<$tgens> DerefMut for $ttype {
127        fn deref_mut(&mut self) -> &mut $ftype {
128            &mut self.$fname
129        }
130    }
131
132    impl<$tgens> AsRef<$ftype> for $ttype {
133        fn as_ref(&self) -> &$ftype {
134            &self.$fname
135        }
136    }
137
138    impl<$tgens> AsMut<$ftype> for $ttype {
139        fn as_mut(&mut self) -> &mut $ftype {
140            &mut self.$fname
141        }
142    }
143  }}
144  }}
145}
146
147define_derive_deftly! {
148    use Transparent;
149
150    /// Implement conversion traits for an arbitrary transparent newtype
151    ///
152    /// # Requirements
153    ///
154    ///  * Self should be a single-field struct
155    ///  * Self should have no runtime invariants
156    ///
157    /// # Generated code
158    ///
159    $TRANSPARENT_DOCS_IMPLS
160    ///  * impls of `AsMut<field>`, `AsRef<field>`
161    ///
162    /// # Guidelines
163    ///
164    ///  * the field should be `pub`, with `#[allow(clippy::exhaustive_structs)]`
165    ///  * derive `Hash`, `Debug` and (usually) `Clone`
166    ///  * consider deriving `PartialEq` and `Eq`
167    ///    but for types containing bytes, use [`ConstantTimeEq`],
168    ///    eg with [`#[derive_deftly(BytesTransparent)]`](derive_deftly_template_BytesTransparent)
169    ///    (instead of `Transparent`).
170    ///  * implement `FromStr`, `Display`, `NormalItemArgument`, as required
171    Transparent for struct, beta_deftly:
172
173    $TRANSPARENT_IMPLS
174}
175
176define_derive_deftly! {
177    use Transparent;
178
179    /// Implement `ConstantTimeEq`, `.as_bytes()`, etc., for a transparent newtype around bytes
180    ///
181    /// # Requirements
182    ///
183    ///  * Self should be a single-field struct
184    ///  * Self should deref to `&[u8]` (and to `&mut [u8]`).
185    ///  * (so Self should have no runtime invariants)
186    ///
187    /// # Generated code
188    ///
189    ///  * impls of `ConstantTimeEq`, `Eq`, `PartialEq`, `Ord`, `PartialOrd`
190    ///  * `as_bytes()` method
191    ${TRANSPARENT_DOCS_IMPLS}
192    ///  * impls of `AsMut<field>`, `AsRef<field>`, `AsRef<[u8]>`, `AsMut<[u8]>`
193    ///
194    // We could derive Debug here but then we have to deal with the Fixed's N
195    // which gets quite fiddly.
196    //
197    /// # Guidelines
198    ///
199    ///  * derive `Hash` and write `#[allow(clippy::derived_hash_with_manual_eq)]`
200    ///  * impl `FromStr` and `Display` (if required, which they usually will be)
201    ///  * derive `derive_more::Debug` eg with `#[debug(r#"B64("{self}")"#)]`
202    ///  * `impl NormalItemArgument` if appropriate (ie the representation has no spaces)
203    BytesTransparent for struct, beta_deftly:
204
205    $TRANSPARENT_IMPLS
206
207    impl<$tgens> ConstantTimeEq for $ttype {
208        fn ct_eq(&self, other: &$ttype) -> Choice {
209          $(
210            self.$fname.ct_eq(&other.$fname)
211          )
212        }
213    }
214    $/// `$tname` is `Eq` via its constant-time implementation.
215    impl<$tgens> PartialEq for $ttype {
216        fn eq(&self, other: &$ttype) -> bool {
217            self.ct_eq(other).into()
218        }
219    }
220    impl<$tgens> Eq for $ttype {}
221    impl<$tgens> PartialOrd for $ttype {
222        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
223            Some(self.cmp(other))
224        }
225    }
226    impl<$tgens> Ord for $ttype {
227        fn cmp(&self, other: &Self) -> Ordering {
228          $(
229            self.$fname.cmp(&other.$fname)
230          )
231        }
232    }
233
234    impl<$tgens> $ttype {
235        /// Return the byte array from this object.
236        pub fn as_bytes(&self) -> &[u8] {
237          $(
238            &self.$fname[..]
239          )
240        }
241    }
242
243    impl<$tgens> AsRef<[u8]> for $ttype {
244        fn as_ref(&self) -> &[u8] {
245          $(
246            self.$fname.as_ref()
247          )
248        }
249    }
250
251    impl<$tgens> AsMut<[u8]> for $ttype {
252        fn as_mut(&mut self) -> &mut [u8] {
253          $(
254            self.$fname.as_mut()
255          )
256        }
257    }
258}
259
260/// Types for decoding base64-encoded values.
261mod b64impl {
262    use super::*;
263    use crate::{Error, NetdocErrorKind as EK, Pos, Result};
264    use base64ct::{Base64, Base64Unpadded, Encoding};
265    use std::ops::RangeBounds;
266
267    /// A byte array, encoded in base64 with optional padding.
268    ///
269    /// On output (`Display`), output is unpadded.
270    #[derive(Clone, Hash, Deftly)]
271    #[derive_deftly(BytesTransparent)]
272    #[allow(clippy::derived_hash_with_manual_eq)]
273    #[derive(derive_more::Debug)]
274    #[debug(r#"B64("{self}")"#)]
275    #[allow(clippy::exhaustive_structs)]
276    pub struct B64(pub Vec<u8>);
277
278    impl FromStr for B64 {
279        type Err = Error;
280        fn from_str(s: &str) -> Result<Self> {
281            let v: core::result::Result<Vec<u8>, base64ct::Error> = match s.len() % 4 {
282                0 => Base64::decode_vec(s),
283                _ => Base64Unpadded::decode_vec(s),
284            };
285            let v = v.map_err(|_| {
286                EK::BadArgument
287                    .with_msg("Invalid base64")
288                    .at_pos(Pos::at(s))
289            })?;
290            Ok(B64(v))
291        }
292    }
293
294    impl Display for B64 {
295        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
296            Display::fmt(&Base64Unpadded::encode_string(&self.0), f)
297        }
298    }
299
300    impl B64 {
301        /// Return this object if its length is within the provided bounds
302        /// object, or an error otherwise.
303        pub(crate) fn check_len<B: RangeBounds<usize>>(self, bounds: B) -> Result<Self> {
304            if bounds.contains(&self.0.len()) {
305                Ok(self)
306            } else {
307                Err(EK::BadObjectVal.with_msg("Invalid length on base64 data"))
308            }
309        }
310
311        /// Try to convert this object into an array of N bytes.
312        ///
313        /// Return an error if the length is wrong.
314        pub(crate) fn into_array<const N: usize>(self) -> Result<[u8; N]> {
315            self.0
316                .try_into()
317                .map_err(|_| EK::BadObjectVal.with_msg("Invalid length on base64 data"))
318        }
319    }
320
321    impl FromIterator<u8> for B64 {
322        fn from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Self {
323            Self(iter.into_iter().collect())
324        }
325    }
326
327    impl NormalItemArgument for B64 {}
328
329    /// A byte array encoded in a hexadecimal with a fixed length.
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, Deftly)]
473    #[derive_deftly(Transparent)]
474    #[allow(clippy::exhaustive_structs)]
475    pub struct Curve25519Public(pub PublicKey);
476
477    impl FromStr for Curve25519Public {
478        type Err = Error;
479        fn from_str(s: &str) -> Result<Self> {
480            let pk: FixedB64<32> = s.parse()?;
481            let pk: [u8; 32] = pk.into();
482            Ok(Curve25519Public(pk.into()))
483        }
484    }
485
486    impl Display for Curve25519Public {
487        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
488            FixedB64::from(self.0.to_bytes()).fmt(f)
489        }
490    }
491
492    impl NormalItemArgument for Curve25519Public {}
493}
494
495// ============================================================
496
497/// Types for decoding ed25519 keys
498mod ed25519impl {
499    use super::*;
500
501    use crate::{Error, NormalItemArgument, Result, types::misc::FixedB64};
502    use derive_deftly::Deftly;
503    use tor_llcrypto::pk::ed25519::{Ed25519Identity, Signature};
504
505    /// An alleged ed25519 public key, encoded in base64 with optional
506    /// padding.
507    #[derive(Debug, Clone, PartialEq, Eq, Deftly)]
508    #[derive_deftly(Transparent)]
509    #[allow(clippy::exhaustive_structs)]
510    pub struct Ed25519Public(pub Ed25519Identity);
511
512    impl FromStr for Ed25519Public {
513        type Err = Error;
514        fn from_str(s: &str) -> Result<Self> {
515            let pk: FixedB64<32> = s.parse()?;
516            Ok(Ed25519Public(Ed25519Identity::new(pk.into())))
517        }
518    }
519
520    impl Display for Ed25519Public {
521        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
522            let pk: [u8; 32] = self.0.into();
523            let pk = FixedB64::from(pk);
524            pk.fmt(f)
525        }
526    }
527
528    impl NormalItemArgument for Ed25519Public {}
529
530    /// Helper that checks for the presence of `ed25519`.
531    #[derive(Debug, Clone, PartialEq, Eq, derive_more::Display, derive_more::FromStr)]
532    #[display(rename_all = "lowercase")]
533    #[from_str(rename_all = "lowercase")]
534    #[allow(clippy::exhaustive_enums)]
535    pub enum Ed25519AlgorithmString {
536        /// Ed25519 encoded as `ed25519`.
537        Ed25519,
538    }
539
540    impl NormalItemArgument for Ed25519AlgorithmString {}
541
542    /// Ed25519 public key in the form `<keyword> id <base64>`
543    ///
544    ///  * `id` in microdescriptors:
545    ///    <https://spec.torproject.org/dir-spec/computing-microdescriptors.html>
546    ///
547    ///  * `identity-ed25519` in routerdescs:
548    ///    <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:identity-ed25519>
549    ///
550    ///  * `id` in votes' routerstatus entries:
551    ///    <https://spec.torproject.org/dir-spec/consensus-formats.html#item:id>
552    #[derive(Debug, Clone, PartialEq, Eq, Deftly)]
553    #[derive_deftly(ItemValueEncodable, ItemValueParseable)]
554    #[non_exhaustive]
555    pub struct Ed25519IdentityLine {
556        /// Fixed magic identifier (`ed25519`) for this line.
557        pub alg: Ed25519AlgorithmString,
558
559        /// The actual Ed25519 identity.
560        pub pk: Ed25519Public,
561    }
562
563    impl From<Ed25519Public> for Ed25519IdentityLine {
564        fn from(pk: Ed25519Public) -> Self {
565            Self {
566                alg: Ed25519AlgorithmString::Ed25519,
567                pk,
568            }
569        }
570    }
571
572    impl From<Ed25519Identity> for Ed25519IdentityLine {
573        fn from(pk: Ed25519Identity) -> Self {
574            Ed25519Public(pk).into()
575        }
576    }
577
578    impl ItemArgument for Signature {
579        fn write_arg_onto(&self, out: &mut ItemEncoder) -> StdResult<(), Bug> {
580            FixedB64::from(self.to_bytes()).write_arg_onto(out)
581        }
582    }
583}
584
585// ============================================================
586
587/// Dummy types like [`Ignored`]
588mod ignored_impl {
589    use super::*;
590
591    use crate::parse2::ErrorProblem as EP;
592    use ArgumentError as AE;
593
594    /// Part of a network document, that isn't actually there.
595    ///
596    /// Used as a standin in `ns_type!` calls in various netstatus `each_variety.rs`.
597    /// The effect is as if the field were omitted from the containing type.
598    ///
599    ///  * When used as item(s) (ie, a field type when deriving `NetdocParseable\[Fields\]`):
600    ///    **ignores any number** of items with that field's keyword during parsing,
601    ///    and emits none during encoding.
602    ///
603    ///    (To *reject* documents containing this item, use `Option<Void>`,
604    ///    but note that the spec says unknown items should be ignored,
605    ///    which would normally include items which are merely missing from one variety.)
606    ///
607    ///  * When used as an argument (ie, a field type when deriving `ItemValueParseable`,
608    ///    or with `netdoc(single_arg)`  when deriving `NetdocParseable\[Fields\]`):
609    ///    consumes **no arguments** during parsing, and emits none during encoding.
610    ///
611    ///  * When used as an object field (ie, `netdoc(object)` when deriving `ItemValueParseable`):
612    ///    **rejects** an object - failing the parse if one is present.
613    ///    (Functions similarly to `Option<Void>`, but prefer `NotPresent` as it's clearer.)
614    ///
615    ///  * When used as a sub-document (ie, `netdoc(flatten)` when deriving a document trait),
616    ///    it recognises, and encodes as, no fields.
617    ///
618    /// There are bespoke impls of the multiplicity traits
619    /// `ItemSetMethods` and `ObjectSetMethods`:
620    /// don't wrap this type in `Option` or `Vec`.
621    //
622    // TODO we'll need to implement ItemArgument etc., for encoding, too.
623    #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Default)]
624    #[allow(clippy::exhaustive_structs)]
625    #[derive(Deftly)]
626    #[derive_deftly(NetdocEncodableFields, NetdocParseableFields)]
627    pub struct NotPresent;
628
629    /// An individual value that is not present - placeholder type
630    ///
631    /// This is the "single" item type for encoding multiplicity
632    /// (for Items, Arguments or Objects), for [`NotPresent`].
633    ///
634    /// It should not be used directly.
635    ///
636    /// During parsing, each "not present" item is ignored,
637    /// but the multiplicity arrangements involve parsing each value
638    /// and then passing the item value to [`ItemSetMethods::accumulate`]
639    /// where (for [`NotPresentEachValue`]) it is discarded.
640    /// Therefore this type must be inhabited; the item parser discards the unparsed item.
641    ///
642    /// During parsing of arguments, parsing is driven by
643    /// [our `ArgumentSetMethods::parse_with`][`P2MultiplicitySelector::<NotPresent>::parse_with)
644    /// which doesn't need to call any parser.
645    /// So the [`ItemArgumentParseable`] implementation always throws an error.
646    ///
647    /// During parsing of objects, rejection is done by
648    /// [`NotPresentEachValue::check_label`] (and `from_bytes`).
649    ///
650    /// For encoding, there is only one multiplicity system which
651    /// will never call any encoding function, so the encoding functions all throw `Bug`.
652    ///
653    /// This type has a similar role to `IgnoredItemOrObjectValue`,
654    /// but `NotPresentEachValue` is different in detail,
655    /// and (unlike `Ignored`) must support arguments, not just items and objects.
656    #[derive(Debug, Clone, Deftly)]
657    #[non_exhaustive]
658    #[derive_deftly(ItemValueParseable, NetdocParseableFields)]
659    pub struct NotPresentEachValue;
660
661    /// Ignored part of a network document.
662    ///
663    /// With `parse2`, can be used as an item, object, or even flattened-fields.
664    ///
665    /// When deriving `parse2` traits, and a field is absent in a particular netstatus variety,
666    /// use `ns_type!` with [`NotPresent`], rather than `Ignored`.
667    ///
668    /// During encoding as an Items or Objects, will be entirely omitted,
669    /// via the multiplicity arrangements.
670    ///
671    /// Cannot be encoded as an Argument: if this is not the last
672    /// Argument, we need something to put into the output document to avoid generating
673    /// a document with the arguments out of step.  If it *is* the last argument,
674    /// it could simply be omitted, since additional arguments are in any case ignored.
675    #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Default, Deftly)]
676    #[derive_deftly(ItemValueParseable, NetdocParseableFields)]
677    #[allow(clippy::exhaustive_structs)]
678    pub struct Ignored;
679
680    /// An Item or Object that would be ignored during parsing and is omitted during encoding
681    ///
682    /// This is the "single" item type for encoding multiplicity for Items or Objects,
683    /// for [`Ignored`].
684    ///
685    /// It should not be used directly.
686    ///
687    /// This type is uninhabited.
688    pub struct IgnoredItemOrObjectValue(Void);
689
690    /// Indicates that no further arguments are allowed in a network document Item line
691    ///
692    /// Unlike [`NotPresent`], this fails during parsing if there are any more arguments.
693    ///
694    /// Should appear only at the end of the argument list.
695    #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Default)]
696    #[allow(clippy::exhaustive_structs)]
697    pub struct NoMoreArguments;
698
699    /// An item that only matters in terms of presence of absence.
700    ///
701    /// Useful for items such as `tunnelled-dir-server` where the mere presence
702    /// implies a truthful value.
703    ///
704    /// This wrapper implements [`ItemValueParseable`] and [`ItemValueEncodable`]
705    /// rejecting all arguments and objects and just expecting/emitting the
706    /// keyword (or not).
707    ///
708    /// # Examples
709    ///
710    /// The following shows an except from a hypothetical netdoc with a
711    /// [`ItemPresent`] item.
712    ///
713    /// ```
714    /// use derive_deftly::Deftly;
715    /// use tor_netdoc::types::*;
716    /// use tor_netdoc::parse2::*;
717    /// use tor_netdoc::*;
718    ///
719    /// #[derive(Debug, Default)]
720    /// struct Hello;
721    ///
722    /// #[derive(Deftly, Debug)]
723    /// #[derive_deftly(NetdocParseable)]
724    /// struct TestDoc {
725    ///     intro: Ignored,
726    ///     hello: Option<ItemPresent<Hello>>,
727    /// }
728    ///
729    /// // hello is not present.
730    /// let doc = parse_netdoc::<TestDoc>(&ParseInput::new("intro\n", "")).unwrap();
731    /// assert!(doc.hello.is_none());
732    ///
733    /// // hello is present.
734    /// let doc = parse_netdoc::<TestDoc>(&ParseInput::new("intro\nhello\n", "")).unwrap();
735    /// assert!(doc.hello.is_some());
736    ///
737    /// // hello has arguments which are ignored.
738    /// let doc = parse_netdoc::<TestDoc>(&ParseInput::new("intro\nhello world\n", "")).unwrap();
739    /// assert!(doc.hello.is_some());
740    ///
741    /// // hello is present twice which is not allowed.
742    /// let doc = parse_netdoc::<TestDoc>(&ParseInput::new("intro\nhello\nhello\n", "")).unwrap_err();
743    /// ```
744    //
745    // We cannot derive Transparent here, because it is not possible to
746    // implement `From<ItemPresent<T>> for T` due to orphan rule.
747    //
748    // Otherwise, a downstream crate could for example implement
749    // `From<ItemPresent<U>> for U` with `U` being a locally defined type,
750    // leading to a conflicting implementation.  A solution would be to cover
751    // `T` behind another generic type such as `PhantomData`, as this can't be
752    // a type in a downstream crate, but that level of indirection feels wrong.
753    #[derive(Debug, Copy, Clone, Default, Ord, PartialOrd, Eq, PartialEq, Hash)]
754    //
755    #[derive(
756        derive_more::From,
757        derive_more::Deref,
758        derive_more::DerefMut,
759        derive_more::AsRef,
760        derive_more::AsMut,
761    )]
762    #[allow(clippy::exhaustive_structs)]
763    pub struct ItemPresent<T: Default>(pub T);
764
765    impl ItemSetMethods for P2MultiplicitySelector<NotPresent> {
766        type Each = NotPresentEachValue;
767        type Field = NotPresent;
768        fn can_accumulate(self, _acc: &Option<NotPresent>) -> Result<(), EP> {
769            Ok(())
770        }
771        fn accumulate(self, _: &mut Option<NotPresent>, _: NotPresentEachValue) -> Result<(), EP> {
772            Ok(())
773        }
774        fn finish(self, _acc: Option<NotPresent>, _: &'static str) -> Result<NotPresent, EP> {
775            Ok(NotPresent)
776        }
777        fn debug_core(self) -> &'static str {
778            "Ignored"
779        }
780    }
781
782    impl ItemValueEncodable for NotPresentEachValue {
783        fn write_item_value_onto(&self, _out: ItemEncoder) -> Result<(), Bug> {
784            Err(internal!("NotPresentEachValue as ItemValueEncodable"))
785        }
786    }
787
788    impl ArgumentSetMethods for P2MultiplicitySelector<NotPresent> {
789        type Each = NotPresentEachValue;
790        type Field = NotPresent;
791
792        fn parse_with<P>(self, _: &mut ArgumentStream<'_>, _: P) -> Result<Self::Field, AE>
793        where
794            P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result<Self::Each, AE>,
795        {
796            Ok(NotPresent)
797        }
798
799        fn debug_core(self) -> &'static str {
800            "NotPresent"
801        }
802    }
803    impl ItemArgument for NotPresentEachValue {
804        fn write_arg_onto(&self, _out: &mut ItemEncoder) -> Result<(), Bug> {
805            Err(internal!("NotPresentEachValue as ItemArgument"))
806        }
807    }
808    impl ItemArgumentParseable for NotPresentEachValue {
809        fn from_args<'s>(_: &mut ArgumentStream<'s>) -> Result<Self, ArgumentError> {
810            // Not quite the right error, but we don't have an ArgumentError::Internal
811            Err(AE::Unexpected)
812        }
813    }
814
815    impl ItemObjectEncodable for NotPresentEachValue {
816        fn label(&self) -> &str {
817            "INTERNAL ERROR"
818        }
819        fn write_object_onto(&self, _b: &mut Vec<u8>) -> Result<(), Bug> {
820            Err(internal!("NotPresentEachValue as ItemObjectEncodable"))
821        }
822    }
823
824    impl ObjectSetMethods for P2MultiplicitySelector<NotPresent> {
825        type Field = NotPresent;
826        type Each = NotPresentEachValue;
827        fn resolve_option(self, _found: Option<NotPresentEachValue>) -> Result<NotPresent, EP> {
828            Ok(NotPresent)
829        }
830        fn debug_core(self) -> &'static str {
831            "NotPresent"
832        }
833    }
834    impl ItemObjectParseable for NotPresentEachValue {
835        fn check_label(_label: &str) -> Result<(), EP> {
836            Err(EP::ObjectUnexpected)
837        }
838        fn from_bytes(_input: &[u8]) -> Result<Self, EP> {
839            Err(EP::ObjectUnexpected)
840        }
841    }
842
843    impl<'f> encode::MultiplicityMethods<'f> for EMultiplicitySelector<NotPresent> {
844        type Field = NotPresent;
845        type Each = NotPresentEachValue;
846        fn iter_ordered(self, _: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
847            iter::empty()
848        }
849    }
850
851    impl encode::OptionalityMethods for EMultiplicitySelector<NotPresent> {
852        type Field = NotPresent;
853        type Each = NotPresentEachValue;
854        fn as_option<'f>(self, _: &'f Self::Field) -> Option<&'f Self::Each> {
855            None
856        }
857    }
858
859    impl FromStr for Ignored {
860        type Err = Void;
861        fn from_str(_s: &str) -> Result<Ignored, Void> {
862            Ok(Ignored)
863        }
864    }
865
866    impl ItemArgumentParseable for Ignored {
867        fn from_args(_: &mut ArgumentStream) -> Result<Ignored, ArgumentError> {
868            Ok(Ignored)
869        }
870    }
871
872    impl ItemObjectParseable for Ignored {
873        fn check_label(_label: &str) -> Result<(), EP> {
874            // allow any label
875            Ok(())
876        }
877        fn from_bytes(_input: &[u8]) -> Result<Self, EP> {
878            Ok(Ignored)
879        }
880    }
881
882    impl ObjectSetMethods for P2MultiplicitySelector<Ignored> {
883        type Field = Ignored;
884        type Each = Ignored;
885        fn resolve_option(self, _found: Option<Ignored>) -> Result<Ignored, EP> {
886            Ok(Ignored)
887        }
888        fn debug_core(self) -> &'static str {
889            "Ignored"
890        }
891    }
892
893    impl<'f> encode::MultiplicityMethods<'f> for EMultiplicitySelector<Ignored> {
894        type Field = Ignored;
895        type Each = IgnoredItemOrObjectValue;
896        fn iter_ordered(self, _: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
897            iter::empty()
898        }
899    }
900
901    impl encode::OptionalityMethods for EMultiplicitySelector<Ignored> {
902        type Field = Ignored;
903        type Each = IgnoredItemOrObjectValue;
904        fn as_option<'f>(self, _: &'f Self::Field) -> Option<&'f Self::Each> {
905            None
906        }
907    }
908
909    impl ItemValueEncodable for IgnoredItemOrObjectValue {
910        fn write_item_value_onto(&self, _: ItemEncoder) -> Result<(), Bug> {
911            void::unreachable(self.0)
912        }
913    }
914
915    impl ItemObjectEncodable for IgnoredItemOrObjectValue {
916        fn label(&self) -> &str {
917            void::unreachable(self.0)
918        }
919        fn write_object_onto(&self, _: &mut Vec<u8>) -> Result<(), Bug> {
920            void::unreachable(self.0)
921        }
922    }
923
924    impl ItemArgumentParseable for NoMoreArguments {
925        fn from_args(args: &mut ArgumentStream) -> Result<NoMoreArguments, ArgumentError> {
926            Ok(args.reject_extra_args()?)
927        }
928    }
929
930    impl ItemArgument for NoMoreArguments {
931        fn write_arg_onto(&self, _: &mut ItemEncoder) -> Result<(), Bug> {
932            Ok(())
933        }
934    }
935
936    impl<T: Default> ItemValueParseable for ItemPresent<T> {
937        fn from_unparsed(item: UnparsedItem<'_>) -> StdResult<Self, EP> {
938            item.check_no_object()?;
939            Ok(Self::default())
940        }
941    }
942
943    impl<T: Default> ItemValueEncodable for ItemPresent<T> {
944        fn write_item_value_onto(&self, out: ItemEncoder) -> StdResult<(), Bug> {
945            out.finish();
946            Ok(())
947        }
948    }
949}
950
951// ============================================================
952
953/// Information about unknown values, which may have been retained as a `T`
954///
955/// Won't grow additional variants - but, `Retained` is only included conditionally.
956///
957/// Also used in the form `Unknown<()>` to indicate whether unknown values *should* be retained.
958///
959/// ### Example
960///
961/// ```
962/// # {
963/// #![cfg(feature = "retain-unknown")]
964///
965/// use tor_netdoc::types::Unknown;
966///
967/// let mut unk: Unknown<Vec<String>> = Unknown::new_retained_default();
968/// unk.with_mut_unknown(|u| u.push("something-we-found".into()));
969/// assert_eq!(unk.into_retained().unwrap(), ["something-we-found"]);
970/// # }
971/// ```
972///
973/// ### Equality comparison, semantics
974///
975/// Two `Unknown` are consider equal if both have the same record of unknown values,
976/// or if neither records unknown values at all.
977///
978/// `Unknown` is not `Eq` or `Ord` because we won't want to relate a `Discarded`
979/// to a `Retained`.  That would be a logic error.  `partial_cmp` gives `None` for this.
980#[derive(Debug, PartialEq, Clone, Copy, Hash)]
981#[allow(clippy::exhaustive_enums)] // this isn't going to change
982pub enum Unknown<T> {
983    /// The parsing discarded unknown values and they are no longer available.
984    Discarded(PhantomData<T>),
985
986    /// The document parsing retained (or should retain) unknown values.
987    #[cfg(feature = "retain-unknown")]
988    Retained(T),
989}
990
991impl<T> Unknown<T> {
992    /// Create an `Unknown` which specifies that values were discarded (or should be)
993    pub fn new_discard() -> Self {
994        Unknown::Discarded(PhantomData)
995    }
996
997    /// Map the `Retained`, if there is one
998    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Unknown<U> {
999        self.try_map(move |t| Ok::<_, Void>(f(t))).void_unwrap()
1000    }
1001
1002    /// Map the `Retained`, fallibly
1003    pub fn try_map<U, E>(self, f: impl FnOnce(T) -> Result<U, E>) -> Result<Unknown<U>, E> {
1004        Ok(match self {
1005            Unknown::Discarded(_) => Unknown::Discarded(PhantomData),
1006            #[cfg(feature = "retain-unknown")]
1007            Unknown::Retained(t) => Unknown::Retained(f(t)?),
1008        })
1009    }
1010
1011    /// Obtain an `Unknown` containing (maybe) a reference
1012    pub fn as_ref(&self) -> Unknown<&T> {
1013        match self {
1014            Unknown::Discarded(_) => Unknown::Discarded(PhantomData),
1015            #[cfg(feature = "retain-unknown")]
1016            Unknown::Retained(t) => Unknown::Retained(t),
1017        }
1018    }
1019
1020    /// Return the retained unknown data, giving `None` if none was saved
1021    ///
1022    /// This is the function for disregarding the possible previously existence
1023    /// of now-discarded unknown (unrecognised) information.
1024    ///
1025    /// Use [`into_retained`](Self::into_retained) if it would be a bug
1026    /// if unrecognised information had been previously discarded.
1027    pub fn only_known(self) -> Option<T> {
1028        match self {
1029            Unknown::Discarded(_) => None,
1030            #[cfg(feature = "retain-unknown")]
1031            Unknown::Retained(t) => Some(t),
1032        }
1033    }
1034
1035    /// Obtain the `Retained` data
1036    ///
1037    /// Treats lack of retention as an internal error.
1038    pub fn into_retained(self) -> Result<T, Bug> {
1039        match self {
1040            Unknown::Discarded(_) => Err(internal!("Unknown::retained but data not collected")),
1041            #[cfg(feature = "retain-unknown")]
1042            Unknown::Retained(t) => Ok(t),
1043        }
1044    }
1045
1046    /// Start recording unknown information, with a default value for `T`
1047    #[cfg(feature = "retain-unknown")]
1048    pub fn new_retained_default() -> Self
1049    where
1050        T: Default,
1051    {
1052        Unknown::Retained(T::default())
1053    }
1054
1055    /// Update the `Retained`, if there is one
1056    ///
1057    /// Intended for use in parsing, when we encounter an unknown value.
1058    ///
1059    /// Not provided in `try_` form.  If you think you need this, instead, unconditionally
1060    /// parse and verify the unknown value, and then conditionally insert it with this function.
1061    /// Don't parse it conditionally - that would skip some validation.
1062    pub fn with_mut_unknown(&mut self, f: impl FnOnce(&mut T)) {
1063        match self {
1064            Unknown::Discarded(_) => {}
1065            #[cfg(feature = "retain-unknown")]
1066            Unknown::Retained(t) => f(t),
1067        }
1068    }
1069}
1070
1071impl<T: PartialOrd> PartialOrd for Unknown<T> {
1072    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1073        use Unknown::*;
1074        match (self, other) {
1075            (Discarded(_), Discarded(_)) => Some(cmp::Ordering::Equal),
1076            #[cfg(feature = "retain-unknown")]
1077            (Discarded(_), Retained(_)) | (Retained(_), Discarded(_)) => None,
1078            #[cfg(feature = "retain-unknown")]
1079            (Retained(a), Retained(b)) => a.partial_cmp(b),
1080        }
1081    }
1082}
1083
1084// ============================================================
1085
1086/// A finite floating point number
1087///
1088/// Suitable for `stats` items in voites' routerstatus entries:
1089/// <https://spec.torproject.org/dir-spec/consensus-formats.html#item:stats>
1090///
1091/// Invariants:
1092///
1093///  * Is finite.  (So not NaN or Inf.)  Might be denormal.
1094///
1095/// String representation:
1096///
1097///  * Parses any valid C-like notation.
1098///
1099///  * Never uses exponential notation to display.
1100///
1101///  * Output can be rather large, up to 326 characters!
1102///    This is a spec bug.  The spec forbids us from using exponential notation.
1103///    <https://gitlab.torproject.org/tpo/core/torspec/-/work_items/416>
1104///
1105/// We may to change this in the future to use exponentials notation for output.
1106/// See <https://gitlab.torproject.org/tpo/core/torspec/-/work_items/416>
1107//
1108// TODO torspec#416 Consider replacing our F64Finite with finite f64 newtype from some crate
1109//
1110// What a palaver!
1111//
1112// This type is here rather than in rs.rs, in case similar things appears in other documents.
1113#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] //
1114#[derive(derive_more::Deref, derive_more::Into, derive_more::Display)]
1115pub struct F64Finite(f64);
1116
1117/// Error converting an [`F64Finite`] from an `f64`: the value wasn't finite
1118#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error, amplify::Getters)]
1119#[error("FP value {} ({bits:#x}) is not finite", f64::from_bits(self.bits))]
1120pub struct F64FiniteError {
1121    /// The raw bits (as from [`f64::to_bits`])
1122    //
1123    // We store it this way rather than as `f64` so that `Eq` etc. make sense.
1124    bits: u64,
1125}
1126
1127impl TryFrom<f64> for F64Finite {
1128    type Error = F64FiniteError;
1129
1130    fn try_from(v: f64) -> Result<Self, F64FiniteError> {
1131        v.is_finite()
1132            .then_some(F64Finite(v))
1133            .ok_or_else(|| F64FiniteError { bits: v.to_bits() })
1134    }
1135}
1136
1137/// Error parsing [`F64Finite`] from a string
1138#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
1139#[non_exhaustive]
1140pub enum F64FiniteParseError {
1141    /// Syntax error
1142    #[error("syntax error")]
1143    Syntax(#[from] std::num::ParseFloatError),
1144
1145    /// Value is not finite
1146    #[error("bad value")]
1147    NotFinite(#[from] F64FiniteError),
1148}
1149
1150impl FromStr for F64Finite {
1151    type Err = F64FiniteParseError;
1152
1153    fn from_str(s: &str) -> StdResult<Self, F64FiniteParseError> {
1154        Ok(s.parse::<f64>()?.try_into()?)
1155    }
1156}
1157
1158impl Eq for F64Finite {}
1159
1160#[allow(clippy::derive_ord_xor_partial_ord)]
1161impl Ord for F64Finite {
1162    fn cmp(&self, other: &F64Finite) -> cmp::Ordering {
1163        self.0
1164            .partial_cmp(&other.0)
1165            .expect("finite f64 partial_cmp gave None")
1166    }
1167}
1168
1169impl NormalItemArgument for F64Finite {}
1170
1171// ============================================================
1172
1173/// Known keyword (enum) value, or arbitrary string
1174///
1175/// `T` should be a `Copy` enum with unit variants.
1176/// It should have appropriate `FromStr` and `Display`,
1177/// as well as [`NormalItemArgument`], impls.
1178///
1179/// Then `KeywordOrString` will implement the same traits.
1180///
1181/// Unlike [`Unknown`], unknown values are always retained as strings.
1182//
1183// `RelayFlags` has machinery for parsing flags and retaining unknown values,
1184// but it uses `Unknown` to maybe discard unknown flags,
1185// and it is generally quite a lot more complicated.
1186#[derive(Debug, PartialEq, Clone, Hash)]
1187#[allow(clippy::exhaustive_enums)] // this isn't going to change
1188pub enum KeywordOrString<T: Copy> {
1189    /// Known and recognised `T`
1190    Known(T),
1191
1192    /// Unknown value in arbitrary syntax
1193    Unknown(String),
1194}
1195
1196impl<T: Copy + NormalItemArgument> NormalItemArgument for KeywordOrString<T> {}
1197
1198impl<T: Copy + Display> Display for KeywordOrString<T> {
1199    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1200        match self {
1201            KeywordOrString::Known(t) => Display::fmt(t, f),
1202            KeywordOrString::Unknown(s) => Display::fmt(s, f),
1203        }
1204    }
1205}
1206
1207impl<T: Copy + FromStr> FromStr for KeywordOrString<T> {
1208    type Err = Void;
1209    fn from_str(s: &str) -> Result<Self, Void> {
1210        Ok(match s.parse() {
1211            Ok(y) => KeywordOrString::Known(y),
1212            Err(_) => KeywordOrString::Unknown(s.to_owned()),
1213        })
1214    }
1215}
1216
1217// ============================================================
1218
1219/// A sequence of `T` items, with their order retained
1220///
1221/// Normally when a `Vec<T>` appears in a network document,
1222/// we expect the items to be sortable - they must impl [`EncodeOrd`](encode::EncodeOrd).
1223/// When encoding, the output is always sorted.
1224///
1225/// *This* type retains the ordering.
1226///
1227/// Implements the [`encode`] and [`parse2`] item multiplicity traits.
1228#[derive(Debug, Clone, Hash, Deftly, Eq, PartialEq, Educe)]
1229#[educe(Default)]
1230#[derive_deftly(Transparent)]
1231#[allow(clippy::exhaustive_structs)]
1232pub struct RetainedOrderVec<T>(pub Vec<T>);
1233
1234// ============================================================
1235
1236/// Types for decoding times and dates
1237mod timeimpl {
1238    use super::*;
1239    use crate::{Error, NetdocErrorKind as EK, Pos, Result};
1240    use std::time::SystemTime;
1241    use time::{
1242        OffsetDateTime, PrimitiveDateTime, format_description::FormatItem,
1243        macros::format_description,
1244    };
1245
1246    /// A wall-clock time, encoded in Iso8601 format with an intervening
1247    /// space between the date and time.
1248    ///
1249    /// (Example: "2020-10-09 17:38:12")
1250    #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Deftly)]
1251    #[derive_deftly(Transparent)]
1252    #[allow(clippy::exhaustive_structs)]
1253    pub struct Iso8601TimeSp(pub SystemTime);
1254
1255    /// Formatting object for parsing the space-separated Iso8601 format.
1256    const ISO_8601SP_FMT: &[FormatItem] =
1257        format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
1258
1259    impl FromStr for Iso8601TimeSp {
1260        type Err = Error;
1261        fn from_str(s: &str) -> Result<Iso8601TimeSp> {
1262            let d = PrimitiveDateTime::parse(s, &ISO_8601SP_FMT).map_err(|e| {
1263                EK::BadArgument
1264                    .at_pos(Pos::at(s))
1265                    .with_msg(format!("invalid time: {}", e))
1266            })?;
1267            Ok(Iso8601TimeSp(d.assume_utc().into()))
1268        }
1269    }
1270
1271    /// Formats a SystemTime according to the given format description
1272    ///
1273    /// Also converts any time::error::format to fmt::Error
1274    /// so that it can be unwrapped in the Display trait impl
1275    fn fmt_with(
1276        t: SystemTime,
1277        format_desc: &[FormatItem],
1278    ) -> core::result::Result<String, fmt::Error> {
1279        OffsetDateTime::from(t)
1280            .format(format_desc)
1281            .map_err(|_| fmt::Error)
1282    }
1283
1284    impl Display for Iso8601TimeSp {
1285        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1286            write!(f, "{}", fmt_with(self.0, ISO_8601SP_FMT)?)
1287        }
1288    }
1289
1290    /// A wall-clock time, encoded in ISO8601 format without an intervening
1291    /// space.
1292    ///
1293    /// This represents a specific UTC instant (ie an instant in global civil time).
1294    /// But it may not be able to represent leap seconds.
1295    ///
1296    /// The timezone is not included in the string representation; `+0000` is implicit.
1297    ///
1298    /// (Example: "2020-10-09T17:38:12")
1299    #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Deftly)]
1300    #[derive_deftly(Transparent)]
1301    #[allow(clippy::exhaustive_structs)]
1302    pub struct Iso8601TimeNoSp(pub SystemTime);
1303
1304    /// Formatting object for parsing the space-separated Iso8601 format.
1305    const ISO_8601NOSP_FMT: &[FormatItem] =
1306        format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]");
1307
1308    impl FromStr for Iso8601TimeNoSp {
1309        type Err = Error;
1310        fn from_str(s: &str) -> Result<Iso8601TimeNoSp> {
1311            let d = PrimitiveDateTime::parse(s, &ISO_8601NOSP_FMT).map_err(|e| {
1312                EK::BadArgument
1313                    .at_pos(Pos::at(s))
1314                    .with_msg(format!("invalid time: {}", e))
1315            })?;
1316            Ok(Iso8601TimeNoSp(d.assume_utc().into()))
1317        }
1318    }
1319
1320    impl Display for Iso8601TimeNoSp {
1321        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1322            write!(f, "{}", fmt_with(self.0, ISO_8601NOSP_FMT)?)
1323        }
1324    }
1325
1326    impl crate::NormalItemArgument for Iso8601TimeNoSp {}
1327}
1328
1329/// Types for decoding RSA keys
1330mod rsa {
1331    use super::*;
1332    use crate::{NetdocErrorKind as EK, Pos, Result};
1333    use std::ops::RangeBounds;
1334    use tor_llcrypto::pk::rsa::PublicKey;
1335    use tor_llcrypto::{d::Sha1, pk::rsa::KeyPair};
1336
1337    /// The fixed exponent which we require when parsing any RSA key in a netdoc
1338    //
1339    // TODO this value is duplicated a lot in the v1 parser
1340    pub(crate) const RSA_FIXED_EXPONENT: u32 = 65537;
1341
1342    /// The fixed exponent which we require when parsing any RSA key in a netdoc
1343    //
1344    // TODO this value is duplicated a lot in the v1 parser
1345    pub(crate) const RSA_MIN_BITS: usize = 1024;
1346
1347    /// RSA public key, partially processed by `crate::paarse`.
1348    ///
1349    /// As parsed from a base64-encoded object.
1350    /// They key's properties (exponent and size) haven't been checked.
1351    #[allow(non_camel_case_types)]
1352    #[derive(Clone, Debug)]
1353    pub(crate) struct RsaPublicParse1Helper(PublicKey, Pos);
1354
1355    /// RSA signature using SHA-1 as per "Signing documents" in dir-spec
1356    ///
1357    /// <https://spec.torproject.org/dir-spec/netdoc.html#signing>
1358    ///
1359    /// Used for
1360    /// [`AuthCert::dir-key-certification`](crate::doc::authcert::AuthCert::dir-key-certification),
1361    /// for example.
1362    ///
1363    /// # Caveats
1364    ///
1365    /// This type MUST NOT be used for anomalous signatures
1366    /// such as
1367    /// [`AuthCert::dir_key_crosscert`](crate::doc::authcert::AuthCert::dir_key_crosscert);
1368    /// in that case because `dir_key_crosscert`'s
1369    /// set of allowed object labels includes `ID SIGNATURE` whereas this type
1370    /// is always `SIGNATURE`
1371    #[derive(Debug, Clone, PartialEq, Eq, Deftly)]
1372    #[derive_deftly(ItemValueParseable, ItemValueEncodable)]
1373    #[deftly(netdoc(no_extra_args, signature(hash_accu = Sha1WholeKeywordLine)))]
1374    #[non_exhaustive]
1375    pub struct RsaSha1Signature {
1376        /// The bytes of the signature (base64-decoded).
1377        #[deftly(netdoc(object(label = "SIGNATURE"), with = crate::types::raw_data_object))]
1378        pub signature: Vec<u8>,
1379    }
1380
1381    impl From<RsaPublicParse1Helper> for PublicKey {
1382        fn from(k: RsaPublicParse1Helper) -> PublicKey {
1383            k.0
1384        }
1385    }
1386    impl super::FromBytes for RsaPublicParse1Helper {
1387        fn from_bytes(b: &[u8], pos: Pos) -> Result<Self> {
1388            let key = PublicKey::from_der(b)
1389                .ok_or_else(|| EK::BadObjectVal.with_msg("unable to decode RSA public key"))?;
1390            Ok(RsaPublicParse1Helper(key, pos))
1391        }
1392    }
1393    impl RsaPublicParse1Helper {
1394        /// Give an error if the exponent of this key is not 'e'
1395        pub(crate) fn check_exponent(self, e: u32) -> Result<Self> {
1396            if self.0.exponent_is(e) {
1397                Ok(self)
1398            } else {
1399                Err(EK::BadObjectVal
1400                    .at_pos(self.1)
1401                    .with_msg("invalid RSA exponent"))
1402            }
1403        }
1404        /// Give an error if the length of this key's modulus, in
1405        /// bits, is not contained in 'bounds'
1406        pub(crate) fn check_len<B: RangeBounds<usize>>(self, bounds: B) -> Result<Self> {
1407            if bounds.contains(&self.0.bits()) {
1408                Ok(self)
1409            } else {
1410                Err(EK::BadObjectVal
1411                    .at_pos(self.1)
1412                    .with_msg("invalid RSA length"))
1413            }
1414        }
1415        /// Give an error if the length of this key's modulus, in
1416        /// bits, is not exactly `n`.
1417        pub(crate) fn check_len_eq(self, n: usize) -> Result<Self> {
1418            self.check_len(n..=n)
1419        }
1420    }
1421
1422    impl RsaSha1Signature {
1423        /// Make a signature according to "Signing documents" in the netdoc spec
1424        ///
1425        /// <https://spec.torproject.org/dir-spec/netdoc.html#signing>
1426        ///
1427        /// `NetdocEncoder` should have had the body of the document
1428        /// (everything except the signatures) already encoded.
1429        ///
1430        /// `item_keyword` is the keyword for the signature item.
1431        /// This is needed because different documents use different keywords,
1432        /// and the keyword is covered by the signature (an annoying is a layering violation).
1433        /// See <https://gitlab.torproject.org/tpo/core/torspec/-/issues/322>.
1434        ///
1435        /// # Example
1436        ///
1437        /// ```
1438        /// use derive_deftly::Deftly;
1439        /// use tor_error::Bug;
1440        /// use tor_llcrypto::pk::rsa;
1441        /// use tor_netdoc::derive_deftly_template_NetdocEncodable;
1442        /// use tor_netdoc::encode::{NetdocEncodable, NetdocEncoder};
1443        /// use tor_netdoc::types::RsaSha1Signature;
1444        ///
1445        /// #[derive(Deftly, Default)]
1446        /// #[derive_deftly(NetdocEncodable)]
1447        /// pub struct Document {
1448        ///     pub document_intro_keyword: (),
1449        /// }
1450        /// #[derive(Deftly)]
1451        /// #[derive_deftly(NetdocEncodable)]
1452        /// pub struct DocumentSignatures {
1453        ///     pub document_signature: RsaSha1Signature,
1454        /// }
1455        /// impl Document {
1456        ///     pub fn encode_sign(&self, k: &rsa::KeyPair) -> Result<String, Bug> {
1457        ///         let mut encoder = NetdocEncoder::new();
1458        ///         self.encode_unsigned(&mut encoder)?;
1459        ///         let document_signature =
1460        ///             RsaSha1Signature::new_sign_netdoc(k, &encoder, "document-signature")?;
1461        ///         let sigs = DocumentSignatures { document_signature };
1462        ///         sigs.encode_unsigned(&mut encoder)?;
1463        ///         let encoded = encoder.finish()?;
1464        ///         Ok(encoded)
1465        ///     }
1466        /// }
1467        ///
1468        /// # fn main() -> Result<(), anyhow::Error> {
1469        /// let k = rsa::KeyPair::generate(&mut tor_basic_utils::test_rng::testing_rng())?;
1470        /// let doc = Document::default();
1471        /// let encoded = doc.encode_sign(&k)?;
1472        /// assert!(encoded.starts_with(concat!(
1473        ///     "document-intro-keyword\n",
1474        ///     "document-signature\n",
1475        ///     "-----BEGIN SIGNATURE-----\n",
1476        /// )));
1477        /// # Ok(())
1478        /// # }
1479        /// ```
1480        pub fn new_sign_netdoc(
1481            private_key: &KeyPair,
1482            encoder: &NetdocEncoder,
1483            item_keyword: &str,
1484        ) -> StdResult<Self, Bug> {
1485            let mut h = Sha1::new();
1486            h.update(encoder.text_sofar()?);
1487            h.update(item_keyword);
1488            h.update("\n");
1489            let h = h.finalize();
1490            let signature = private_key
1491                .sign(&h)
1492                .map_err(into_internal!("RSA signing failed"))?;
1493            Ok(RsaSha1Signature { signature })
1494        }
1495    }
1496}
1497
1498/// Types for decoding Ed25519 certificates
1499mod edcert {
1500    use std::result::Result as StdResult;
1501    use std::time::SystemTime;
1502
1503    use crate::types::EmbeddedCert;
1504    use crate::{
1505        NetdocErrorKind as EK, Pos, Result,
1506        parse2::{ErrorProblem, VerifyFailed},
1507        types::EmbeddableCertObject,
1508    };
1509    use tor_cert::{CertType, CertifiedKey, Ed25519Cert, KeyUnknownCert};
1510    use tor_checkable::signed::SignatureGated;
1511    use tor_checkable::timed::TimerangeBound;
1512    use tor_checkable::{SelfSigned, Timebound};
1513    use tor_error::{Bug, into_internal};
1514    use tor_llcrypto::pk::ed25519::{self, Ed25519PublicKey, ValidatableEd25519Signature};
1515
1516    /// An ed25519 certificate as parsed from a directory object, with
1517    /// signature not validated.
1518    #[derive(Debug, Clone)]
1519    pub(crate) struct UnvalidatedEdCert(KeyUnknownCert, Pos);
1520
1521    impl super::FromBytes for UnvalidatedEdCert {
1522        fn from_bytes(b: &[u8], p: Pos) -> Result<Self> {
1523            let cert = Ed25519Cert::decode(b).map_err(|e| {
1524                EK::BadObjectVal
1525                    .at_pos(p)
1526                    .with_msg("Bad certificate")
1527                    .with_source(e)
1528            })?;
1529
1530            Ok(Self(cert, p))
1531        }
1532        fn from_vec(v: Vec<u8>, p: Pos) -> Result<Self> {
1533            Self::from_bytes(&v[..], p)
1534        }
1535    }
1536    impl UnvalidatedEdCert {
1537        /// Give an error if this certificate's type is not `desired_type`.
1538        pub(crate) fn check_cert_type(self, desired_type: CertType) -> Result<Self> {
1539            if self.0.peek_cert_type() != desired_type {
1540                return Err(EK::BadObjectVal.at_pos(self.1).with_msg(format!(
1541                    "bad certificate type {} (wanted {})",
1542                    self.0.peek_cert_type(),
1543                    desired_type
1544                )));
1545            }
1546            Ok(self)
1547        }
1548        /// Give an error if this certificate's subject_key is not `pk`
1549        pub(crate) fn check_subject_key_is(self, pk: &ed25519::Ed25519Identity) -> Result<Self> {
1550            if self.0.peek_subject_key().as_ed25519() != Some(pk) {
1551                return Err(EK::BadObjectVal
1552                    .at_pos(self.1)
1553                    .with_msg("incorrect subject key"));
1554            }
1555            Ok(self)
1556        }
1557        /// Consume this object and return the inner Ed25519 certificate.
1558        pub(crate) fn into_unchecked(self) -> KeyUnknownCert {
1559            self.0
1560        }
1561    }
1562
1563    /// An Ed25519 identity certificate.
1564    ///
1565    /// This is a certificate of [`CertType::IDENTITY_V_SIGNING`] where the
1566    /// relay's long-term ed25519 identity key signs the relay's medium-term
1567    /// ed25519 signing key, used for signing almost all other certifications
1568    /// associated with a given relay.
1569    #[derive(Debug, Clone, PartialEq, Eq)]
1570    #[allow(clippy::exhaustive_structs)]
1571    pub struct Ed25519IdentityCert {
1572        /// The long-term ed25519 identity key of the relay
1573        pub id_ed25519: ed25519::Ed25519Identity,
1574        /// The medium-term ed25519 signing key of the relay.
1575        pub sign_ed25519: ed25519::Ed25519Identity,
1576    }
1577
1578    impl EmbeddableCertObject<KeyUnknownCert> for Ed25519IdentityCert {
1579        const LABEL: &str = "ED25519 CERT";
1580    }
1581
1582    impl Ed25519IdentityCert {
1583        /// Verifies the validity of an [`Ed25519IdentityCert`].
1584        ///
1585        /// # Requirements
1586        ///
1587        /// 1. MUST have the identity key in the `signed-with-ed25519-key` extension.
1588        /// 2. MUST have a valid signature by the identity key.
1589        /// 3. MUST be of [`CertType::IDENTITY_V_SIGNING`].
1590        /// 4. Certified key MUST BE of [`tor_cert::CertifiedKey::Ed25519`].
1591        /// 5. Both keys MUST be valid mappings to a [`ed25519::PublicKey`].
1592        pub fn verify(cert: KeyUnknownCert) -> StdResult<TimerangeBound<Self>, VerifyFailed> {
1593            let cert = cert
1594                // 1. MUST have the identity key in the `signed-with-ed25519-key` extension.
1595                .should_have_signing_key()
1596                .map_err(|_| VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData))?
1597                // 2. MUST have a valid signature by the identity key.
1598                .check_signature()?
1599                // Okay to call because we create TimerangeBound later.
1600                // TODO DIRAUTH: Use TimerangeBound instead.
1601                .dangerously_assume_timely();
1602
1603            // 3. MUST be of [`CertType::IDENTITY_V_SIGNING`].
1604            if cert.cert_type() != CertType::IDENTITY_V_SIGNING {
1605                return Err(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData));
1606            }
1607
1608            // Bug is alright because .should_have_signing_key() assured us.
1609            let id_ed25519 = *cert.signing_key().ok_or(VerifyFailed::Bug)?;
1610
1611            // 4. Certified key MUST BE of [`tor_cert::CertifiedKey::Ed25519`].
1612            let sign_ed25519 = *cert
1613                .subject_key()
1614                .as_ed25519()
1615                .ok_or(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData))?;
1616
1617            // 5. Both keys MUST be valid mappings to a [`ed25519::PublicKey`].
1618            // Unsure if this check is required or implied by (2) but defensive
1619            // programming does not hurt.
1620            if ed25519::PublicKey::try_from(id_ed25519).is_err()
1621                || ed25519::PublicKey::try_from(sign_ed25519).is_err()
1622            {
1623                return Err(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData));
1624            }
1625
1626            Ok(TimerangeBound::new(
1627                Self {
1628                    id_ed25519,
1629                    sign_ed25519,
1630                },
1631                ..cert.expiry(),
1632            ))
1633        }
1634
1635        /// Creates a new signed [`Ed25519IdentityCert`].
1636        pub fn new_signed(
1637            id_ed25519: &ed25519::Keypair,
1638            sign_ed25519: ed25519::Ed25519Identity,
1639            expiry: SystemTime,
1640        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
1641            let cert = Ed25519Cert::builder()
1642                .expiration(expiry)
1643                .signing_key(id_ed25519.public_key().into())
1644                .cert_type(CertType::IDENTITY_V_SIGNING)
1645                .cert_key(sign_ed25519.into())
1646                .encode_and_sign(id_ed25519)
1647                .map_err(into_internal!("failed to encode and sign identity cert"))?;
1648
1649            let cert =
1650                Ed25519Cert::decode(&cert).map_err(into_internal!("decode just encoded cert"))?;
1651
1652            Ok(EmbeddedCert::new(
1653                Self {
1654                    id_ed25519: id_ed25519.public_key().into(),
1655                    sign_ed25519,
1656                },
1657                cert,
1658            ))
1659        }
1660    }
1661
1662    /// An Ed25519 family certificate.
1663    ///
1664    /// This is a certificate of [`CertType::FAMILY_V_IDENTITY`] where the
1665    /// family key signs the long-term ed25519 identity key of the given relay.
1666    ///
1667    /// It purposely does not store the long-term ed25519 identity key of the
1668    /// relay because the idea of this type should be equal only to other types
1669    /// with the same family key.
1670    #[derive(Debug, Clone, PartialEq, Eq)]
1671    #[allow(clippy::exhaustive_structs)]
1672    pub struct Ed25519FamilyCert {
1673        /// The public key of the family.
1674        // TODO: We probably want to add a getter for this returning the
1675        // family name as in:
1676        // <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:family-cert>
1677        pub family_ed25519: ed25519::Ed25519Identity,
1678    }
1679
1680    impl EmbeddableCertObject<KeyUnknownCert> for Ed25519FamilyCert {
1681        const LABEL: &str = "FAMILY CERT";
1682    }
1683
1684    impl Ed25519FamilyCert {
1685        /// Verifies the validity of an [`Ed25519FamilyCert`].
1686        ///
1687        /// For such a certificate to be valid, the caller must provide a
1688        /// known Ed25519 identity key of the relay beforehand.
1689        ///
1690        /// # Requirements
1691        ///
1692        /// 1. MUST have the `signed-with-ed25519-key` extension containing the family key.
1693        /// 2. MUST have a valid signature by the family key.
1694        /// 3. MUST be of of [`CertType::FAMILY_V_IDENTITY`].
1695        /// 4. Certified key MUST BE of [`tor_cert::CertifiedKey::Ed25519`].
1696        /// 5. `id_ed25519` MUST be the certified key.
1697        /// 6. Both keys MUST be valid mappings to a [`ed25519::PublicKey`].
1698        pub fn verify(
1699            id_ed25519: ed25519::Ed25519Identity,
1700            cert: KeyUnknownCert,
1701        ) -> StdResult<TimerangeBound<Self>, VerifyFailed> {
1702            let cert = cert
1703                // 1. MUST have the `signed-with-ed25519-key` extension containing the family key.
1704                .should_have_signing_key()?
1705                // 2. MUST have a valid signature by the family key.
1706                .check_signature()?
1707                // Okay to call because we create TimerangeBound later.
1708                // TODO DIRAUTH: Use TimerangeBound instead.
1709                .dangerously_assume_timely();
1710
1711            // 3. MUST be of of [`CertType::FAMILY_V_IDENTITY`].
1712            if cert.cert_type() != CertType::FAMILY_V_IDENTITY {
1713                return Err(ErrorProblem::ObjectInvalidData.into());
1714            }
1715
1716            // Bug is alright because .should_have_signing_key() assured us.
1717            let family_ed25519 = *cert.signing_key().ok_or(VerifyFailed::Bug)?;
1718
1719            // 4. Certified key MUST BE of [`tor_cert::CertifiedKey::Ed25519`].
1720            let certified_key = *cert
1721                .subject_key()
1722                .as_ed25519()
1723                .ok_or(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData))?;
1724
1725            // 5. `id_ed25519` MUST be the certified key.
1726            if certified_key != id_ed25519 {
1727                return Err(VerifyFailed::VerifyFailed);
1728            }
1729
1730            // 6. Both keys MUST be valid mappings to a [`ed25519::PublicKey`].
1731            if ed25519::PublicKey::try_from(family_ed25519).is_err()
1732                || ed25519::PublicKey::try_from(id_ed25519).is_err()
1733            {
1734                return Err(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData));
1735            }
1736
1737            Ok(TimerangeBound::new(
1738                Self { family_ed25519 },
1739                ..cert.expiry(),
1740            ))
1741        }
1742
1743        /// Creates a new signed [`Ed25519FamilyCert`].
1744        pub fn new_signed(
1745            family_ed25519: &ed25519::Keypair,
1746            id_ed25519: ed25519::Ed25519Identity,
1747            expiry: SystemTime,
1748        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
1749            let cert = Ed25519Cert::builder()
1750                .expiration(expiry)
1751                .signing_key(family_ed25519.public_key().into())
1752                .cert_type(CertType::FAMILY_V_IDENTITY)
1753                .cert_key(id_ed25519.into())
1754                .encode_and_sign(family_ed25519)
1755                .map_err(into_internal!("failed to encode and sign family cert"))?;
1756
1757            let cert =
1758                Ed25519Cert::decode(&cert).map_err(into_internal!("decode just encoded cert"))?;
1759
1760            Ok(EmbeddedCert::new(
1761                Self {
1762                    family_ed25519: family_ed25519.public_key().into(),
1763                },
1764                cert,
1765            ))
1766        }
1767    }
1768
1769    /// Verified reverse cert by K_ntor on KP_relayid_ed
1770    ///
1771    /// This certificate is signed by KS_ntor
1772    /// (the circuit extension key) and certifies
1773    /// KP_relayid_ed25519 ed25519 identity key of the relay.
1774    ///
1775    /// The type itself is zero-sized because it provides no new useful
1776    /// information that cannot be found elsewhere within the router descriptor.
1777    /// It is intended for use within
1778    /// [`EmbeddedCert`]`<Ed25519NtorCrossCert, KeyUnknownCert>`
1779    ///
1780    /// # Note on key conversion
1781    ///
1782    /// Keep in mind however that the ntor onion key is only provided as an
1783    /// X25519 key and *not* an Ed25519 key, meaning that interfacing
1784    /// applications have to convert it using a function such as
1785    /// [`tor_llcrypto::pk::keymanip::convert_curve25519_to_ed25519_public()`].
1786    /// This also requires obtaining the sign bit which is usually given as an
1787    /// argument in the `ntor-onion-key-crosscert` item.  However, this is
1788    /// outside of the scope of this struct and the code will assume that
1789    /// callers have already converted the X25519 public key to an Ed25519
1790    /// public key as outlined in the specifications.
1791    ///
1792    /// # See Also
1793    ///
1794    /// * <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:ntor-onion-key-crosscert>
1795    /// * <https://spec.torproject.org/dir-spec/converting-to-ed25519.html>
1796    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1797    #[non_exhaustive]
1798    pub struct Ed25519NtorCrossCert {
1799        /// Explicit field, to avoid constructing this accidentally without
1800        /// doing all the verification.
1801        _promise_we_verified: (),
1802    }
1803
1804    impl EmbeddableCertObject<KeyUnknownCert> for Ed25519NtorCrossCert {
1805        const LABEL: &str = "ED25519 CERT";
1806    }
1807
1808    impl Ed25519NtorCrossCert {
1809        /// Verifies the validity of an [`Ed25519NtorCrossCert`].
1810        ///
1811        /// For such a certificate to be valid, the caller must provide a known
1812        /// Ed25519 identity key and Ed25519 ntor onion key of the relay
1813        /// beforehand.
1814        ///
1815        /// # Requirements
1816        ///
1817        /// 1. MUST be of [`CertType::NTOR_CC_IDENTITY`].
1818        /// 2. Certified key MUST be of [`CertifiedKey::Ed25519`].
1819        /// 3. Certified key MUST be equal to `id_ed25519`.
1820        /// 4. MUST have a valid signature.
1821        pub fn verify(
1822            ntor_ed25519: ed25519::Ed25519Identity,
1823            id_ed25519: ed25519::Ed25519Identity,
1824            cert: KeyUnknownCert,
1825        ) -> StdResult<TimerangeBound<Self>, VerifyFailed> {
1826            Ok(
1827                // .verify_inner() ensures 1-3.
1828                Self::verify_inner(ntor_ed25519, id_ed25519, cert)?
1829                    .0
1830                    // 4. MUST have a valid signature.
1831                    .check_signature()?,
1832            )
1833        }
1834
1835        /// Creates a new signed [`Ed25519NtorCrossCert`].
1836        pub fn new_signed(
1837            ntor_ed25519: &ed25519::ExpandedKeypair,
1838            id_ed25519: ed25519::Ed25519Identity,
1839            expiry: SystemTime,
1840        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
1841            let cert = Ed25519Cert::builder()
1842                .expiration(expiry)
1843                .cert_type(CertType::NTOR_CC_IDENTITY)
1844                .cert_key(id_ed25519.into())
1845                .encode_and_sign(ntor_ed25519)
1846                .map_err(into_internal!("failed to encode and sign ntor cert"))?;
1847
1848            let cert =
1849                Ed25519Cert::decode(&cert).map_err(into_internal!("decode just encoded cert"))?;
1850
1851            Ok(EmbeddedCert::new(
1852                Self {
1853                    _promise_we_verified: (),
1854                },
1855                cert,
1856            ))
1857        }
1858
1859        /// Verifies the validity of a [`KeyUnknownCert`] believed to be a
1860        /// [`CertType::NTOR_CC_IDENTITY`].
1861        ///
1862        /// This function serves as glue between the legacy parser and
1863        /// [`Self::verify()`].
1864        ///
1865        /// # Requirements
1866        ///
1867        /// 1. MUST be of [`CertType::NTOR_CC_IDENTITY`].
1868        /// 2. Certified key MUST be of [`CertifiedKey::Ed25519`].
1869        /// 3. Certified key MUST be equal to `id_ed25519`.
1870        ///
1871        /// # Return Type
1872        ///
1873        /// Actual signature and time validation is done by the caller, hence
1874        /// why it returns a gated type as the first element of the tuple.
1875        /// The other elements constitute the inner signature plus the
1876        /// SystemTime denoting the expiry.  This is required for integration
1877        /// with legacy parser in order to enable pushing it to the verification
1878        /// batch, as the [`tor_checkable`] primitives do not provide access
1879        /// to the inner signatures/expiries and also do not support operations
1880        /// like cloning due to being dyn.
1881        pub(crate) fn verify_inner(
1882            ntor_ed25519: ed25519::Ed25519Identity,
1883            id_ed25519: ed25519::Ed25519Identity,
1884            cert: KeyUnknownCert,
1885        ) -> StdResult<
1886            (
1887                SignatureGated<TimerangeBound<Self>>,
1888                ValidatableEd25519Signature,
1889                SystemTime,
1890            ),
1891            VerifyFailed,
1892        > {
1893            // 1. MUST be of [`CertType::NTOR_CC_IDENTITY`].
1894            if cert.peek_cert_type() != CertType::NTOR_CC_IDENTITY {
1895                return Err(ErrorProblem::ObjectInvalidData.into());
1896            }
1897
1898            // 2. Certified key MUST be of [`CertifiedKey::Ed25519`].
1899            // 3. Certified key MUST be equal to `id_ed25519`.
1900            if cert.peek_subject_key() != &CertifiedKey::Ed25519(id_ed25519) {
1901                return Err(VerifyFailed::VerifyFailed);
1902            }
1903
1904            // Fish out the signature from the certificate and verify it later.
1905            //
1906            // It may fail if ntor_ed25519 is not a valid mapping to a public
1907            // key.  This is okay.  The .should_be_signed_with() call is
1908            // tor_cert boilerplate and only required to obtain an
1909            // UncheckedCert, as ntor cross-certificates do not contain the
1910            // signed-with extension.
1911            let (cert, sig) = cert
1912                .should_be_signed_with(&ntor_ed25519)?
1913                .dangerously_split()?;
1914
1915            // Fish out the expiration date from the certificate.
1916            //
1917            // Important: We must not set SystemTime::UNIX_EPOCH as the lower
1918            // bound, because with TimerangeBound, a lower-bound of zero is not
1919            // equal to an absent lower bound!
1920            let cert = cert.dangerously_assume_timely();
1921            let expiration = ..cert.expiry();
1922
1923            Ok((
1924                SignatureGated::new(
1925                    TimerangeBound::new(
1926                        Self {
1927                            _promise_we_verified: (),
1928                        },
1929                        expiration,
1930                    ),
1931                    vec![Box::new(sig.clone())],
1932                ),
1933                sig,
1934                expiration.end,
1935            ))
1936        }
1937
1938        /// Internal function for creating an unverified instance.
1939        ///
1940        /// This is only intended for testing and legacy parser compatibility
1941        /// purposes.
1942        pub(crate) fn dangerous_new_unverified() -> Self {
1943            Self {
1944                _promise_we_verified: (),
1945            }
1946        }
1947    }
1948}
1949
1950/// Digest identifiers, and digests in the form `ALGORITHM=BASE64U`
1951///
1952/// As found in a vote's `m` line.
1953// TODO Use FixedB64 here.
1954mod identified_digest {
1955    use super::*;
1956
1957    define_derive_deftly! {
1958        /// impl `FromStr` and `Display` for an enum with unit variants but also "unknown"
1959        ///
1960        /// Expected input: an enum whose variants are either
1961        ///  * unit variants, perhaps with `#[deftly(string_repr = "string")]`
1962        ///  * singleton tuple variant, containing `String` (or near equivalent)
1963        ///
1964        /// If `#[deftly(string_repro)]` is not specified,
1965        /// the default is snake case of the variant name.
1966        //
1967        // This macro may seem overkill, but open-coding these impls gives opportunities
1968        // for mismatches between FromStr, Display, and the variant name.
1969        //
1970        // TODO consider putting this in tor-basic-utils (maybe with a better name),
1971        // or possibly asking if derive_more want their FromStr to have this.
1972        StringReprUnitsOrUnknown for enum, expect items, beta_deftly:
1973
1974        ${define STRING_REPR {
1975            ${vmeta(string_repr)
1976              as str,
1977              default { ${concat ${snake_case $vname}} }
1978            }
1979        }}
1980
1981        impl FromStr for $ttype {
1982            type Err = Void;
1983            fn from_str(s: &str) -> Result<Self, Void> {
1984                $(
1985                    ${when v_is_unit}
1986                    if s == $STRING_REPR {
1987                        return Ok($vtype)
1988                    }
1989                )
1990                $(
1991                    ${when not(v_is_unit)} // anything else had better be Unknown
1992                    // not using `return ..;` makes this a syntax error if there are several.
1993                    Ok($vtype { 0: s.into() })
1994                )
1995            }
1996        }
1997        impl AsRef<str> for $ttype {
1998            fn as_ref(&self) -> &str {
1999                match self {
2000                    $(
2001                        ${when v_is_unit}
2002                        $vtype => $STRING_REPR,
2003                    )
2004                    $(
2005                        ${when not(v_is_unit)}
2006                        $vpat => f_0,
2007                    )
2008                }
2009            }
2010        }
2011        impl Display for $ttype {
2012            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2013                let s: &str = self.as_ref();
2014                Display::fmt(s, f)
2015            }
2016        }
2017    }
2018
2019    /// The name of a digest algorithm.
2020    ///
2021    /// Can represent an unrecognised algorithm, so it's parsed and reproduced.
2022    #[derive(Debug, Clone, Eq, PartialEq, Hash, Deftly)]
2023    #[derive_deftly(StringReprUnitsOrUnknown)]
2024    #[non_exhaustive]
2025    pub enum DigestName {
2026        /// SHA-256
2027        Sha256,
2028        /// Unknown
2029        Unknown(String),
2030    }
2031
2032    /// A single digest made with a nominated digest algorithm, `ALGORITHM=DIGEST`
2033    #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, derive_more::Display)]
2034    #[display("{alg}={value}")]
2035    #[non_exhaustive]
2036    pub struct IdentifiedDigest {
2037        /// The algorithm name.
2038        alg: DigestName,
2039
2040        /// The digest value.
2041        ///
2042        /// Invariant: length is correct for `alg`, assuming `alg` is known.
2043        value: B64,
2044    }
2045
2046    impl NormalItemArgument for DigestName {}
2047    impl NormalItemArgument for IdentifiedDigest {}
2048
2049    /// Invalid syntax parsing an `IdentifiedDigest`
2050    #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, thiserror::Error)]
2051    #[error("invalid syntax, expected ALGORITHM=DIGEST: {0}")]
2052    pub struct IdentifiedDigestParseError(String);
2053
2054    impl Ord for DigestName {
2055        fn cmp(&self, other: &Self) -> Ordering {
2056            self.as_ref().cmp(other.as_ref())
2057        }
2058    }
2059    impl PartialOrd for DigestName {
2060        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2061            Some(self.cmp(other))
2062        }
2063    }
2064
2065    impl FromStr for IdentifiedDigest {
2066        type Err = IdentifiedDigestParseError;
2067
2068        fn from_str(s: &str) -> Result<Self, Self::Err> {
2069            (|| {
2070                let (alg, value) = s.split_once('=').ok_or("missing equals sign")?;
2071
2072                let alg = alg.parse().void_unwrap();
2073                let value = value
2074                    .parse::<B64>()
2075                    .map_err(|e| format!("bad value: {}", e.report()))?;
2076
2077                if let Some(exp_len) = (|| {
2078                    Some({
2079                        use DigestName::*;
2080                        match alg {
2081                            Sha256 => 32,
2082                            Unknown(_) => None?,
2083                        }
2084                    })
2085                })() {
2086                    let val_len = value.as_bytes().len();
2087                    if val_len != exp_len {
2088                        return Err(format!("got {val_len} bytes, expected {exp_len}"));
2089                    }
2090                }
2091
2092                Ok(IdentifiedDigest { alg, value })
2093            })()
2094            .map_err(IdentifiedDigestParseError)
2095        }
2096    }
2097}
2098
2099/// Types for decoding RSA fingerprints
2100mod fingerprint {
2101    use super::*;
2102    use crate::parse2::{ArgumentError, ArgumentStream, ItemArgumentParseable};
2103    use crate::{Error, NetdocErrorKind as EK, Pos, Result};
2104    use base64ct::{Base64Unpadded, Encoding as _};
2105    use itertools::Itertools;
2106    use tor_llcrypto::pk::rsa::RsaIdentity;
2107
2108    /// A hex-encoded RSA key identity (fingerprint) with spaces in it.
2109    ///
2110    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html?highlight=fingerprint#item:fingerprint>
2111    ///
2112    /// Netdoc parsing adapter for [`RsaIdentity`]
2113    #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
2114    #[derive_deftly(Transparent)]
2115    #[allow(clippy::exhaustive_structs)]
2116    pub struct SpFingerprint(pub RsaIdentity);
2117
2118    /// A hex-encoded fingerprint with no spaces.
2119    ///
2120    /// Netdoc parsing adapter for [`RsaIdentity`]
2121    #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
2122    #[derive_deftly(Transparent)]
2123    #[allow(clippy::exhaustive_structs)]
2124    pub struct Fingerprint(pub RsaIdentity);
2125
2126    /// A base64-encoded fingerprint (unpadded)
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 Base64Fingerprint(pub RsaIdentity);
2133
2134    /// A "long identity" in the format used for Family members.
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(crate) struct LongIdent(pub RsaIdentity);
2141
2142    /// Helper: parse an identity from a hexadecimal string
2143    fn parse_hex_ident(s: &str) -> Result<RsaIdentity> {
2144        RsaIdentity::from_hex(s).ok_or_else(|| {
2145            EK::BadArgument
2146                .at_pos(Pos::at(s))
2147                .with_msg("wrong length on fingerprint")
2148        })
2149    }
2150
2151    impl FromStr for SpFingerprint {
2152        type Err = Error;
2153        fn from_str(s: &str) -> Result<SpFingerprint> {
2154            let ident = parse_hex_ident(&s.replace(' ', "")).map_err(|e| e.at_pos(Pos::at(s)))?;
2155            Ok(SpFingerprint(ident))
2156        }
2157    }
2158
2159    impl ItemArgumentParseable for SpFingerprint {
2160        fn from_args<'s>(
2161            args: &mut ArgumentStream<'s>,
2162        ) -> std::result::Result<Self, ArgumentError> {
2163            // Take the first 10 arguments because an SpFingerprint consists of
2164            // 10 x 4 = 40 characters.
2165            let fp = args.take(10).collect::<Vec<_>>();
2166
2167            // Less than 10 means missing arguments.
2168            if fp.len() < 10 {
2169                return Err(ArgumentError::Missing);
2170            }
2171
2172            // More than 10 should be impossible due to .take(10).
2173            debug_assert_eq!(fp.len(), 10);
2174
2175            // All arguments must be 4 characters long.
2176            if fp.iter().any(|arg| arg.len() != 4) {
2177                return Err(ArgumentError::Invalid);
2178            }
2179
2180            // Convert it to a string without spaces, RsaIdentity::from_hex will
2181            // verify the rest.
2182            Ok(Self(
2183                RsaIdentity::from_hex(fp.join("").as_str()).ok_or(ArgumentError::Invalid)?,
2184            ))
2185        }
2186    }
2187
2188    impl encode::ItemArgument for SpFingerprint {
2189        fn write_arg_onto(&self, out: &mut ItemEncoder<'_>) -> StdResult<(), Bug> {
2190            let res = self
2191                .0
2192                .to_bytes()
2193                .chunks(2)
2194                .map(|b| format!("{:02X}{:02X}", b[0], b[1]))
2195                .join(" ");
2196            debug_assert_eq!(res.len(), 4 * 10 + 9);
2197            out.args_raw_string(&res);
2198            Ok(())
2199        }
2200    }
2201
2202    impl FromStr for Base64Fingerprint {
2203        type Err = Error;
2204        fn from_str(s: &str) -> Result<Base64Fingerprint> {
2205            let b = s.parse::<super::B64>()?;
2206            let ident = RsaIdentity::from_bytes(b.as_bytes()).ok_or_else(|| {
2207                EK::BadArgument
2208                    .at_pos(Pos::at(s))
2209                    .with_msg("Wrong identity length")
2210            })?;
2211            Ok(Base64Fingerprint(ident))
2212        }
2213    }
2214
2215    impl Display for Base64Fingerprint {
2216        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2217            Display::fmt(&Base64Unpadded::encode_string(self.as_bytes()), f)
2218        }
2219    }
2220
2221    impl FromStr for Fingerprint {
2222        type Err = Error;
2223        fn from_str(s: &str) -> Result<Fingerprint> {
2224            let ident = parse_hex_ident(s).map_err(|e| e.at_pos(Pos::at(s)))?;
2225            Ok(Fingerprint(ident))
2226        }
2227    }
2228
2229    impl Display for Fingerprint {
2230        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2231            Display::fmt(&hex::encode_upper(self.as_bytes()), f)
2232        }
2233    }
2234
2235    impl FromStr for LongIdent {
2236        type Err = Error;
2237        fn from_str(mut s: &str) -> Result<LongIdent> {
2238            s = s.strip_prefix('$').unwrap_or(s);
2239            // Strip at '=' or '~' if found.
2240            s = s.split_once(['=', '~']).map(|(a, _)| a).unwrap_or(s);
2241            let ident = parse_hex_ident(s)?;
2242            Ok(LongIdent(ident))
2243        }
2244    }
2245
2246    impl Display for LongIdent {
2247        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2248            write!(f, "${}", self.0.as_hex_upper())
2249        }
2250    }
2251
2252    impl crate::NormalItemArgument for Fingerprint {}
2253    impl crate::NormalItemArgument for Base64Fingerprint {}
2254    impl crate::NormalItemArgument for LongIdent {}
2255}
2256
2257/// A type for relay nicknames
2258mod nickname {
2259    use super::*;
2260    use tinystr::TinyAsciiStr;
2261
2262    /// This is a strange limit, but it comes from Tor.
2263    const MAX_NICKNAME_LEN: usize = 19;
2264
2265    /// The nickname for a Tor relay.
2266    ///
2267    /// These nicknames are legacy mechanism that's occasionally useful in
2268    /// debugging. They should *never* be used to uniquely identify relays;
2269    /// nothing prevents two relays from having the same nickname.
2270    ///
2271    /// Nicknames are required to be ASCII, alphanumeric, and between 1 and 19
2272    /// characters inclusive.
2273    #[derive(Clone, Debug, PartialEq, Eq)]
2274    pub struct Nickname(tinystr::TinyAsciiStr<MAX_NICKNAME_LEN>);
2275
2276    /// Invalid nickname
2277    #[derive(Clone, Debug, thiserror::Error)]
2278    #[error("invalid nickname")]
2279    #[non_exhaustive]
2280    pub struct InvalidNickname {}
2281
2282    impl Nickname {
2283        /// Return a view of this nickname as a string slice.
2284        pub(crate) fn as_str(&self) -> &str {
2285            self.0.as_str()
2286        }
2287    }
2288
2289    impl Display for Nickname {
2290        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2291            self.as_str().fmt(f)
2292        }
2293    }
2294
2295    impl FromStr for Nickname {
2296        type Err = InvalidNickname;
2297
2298        fn from_str(s: &str) -> Result<Self, InvalidNickname> {
2299            let tiny = TinyAsciiStr::from_str(s).map_err(|_| InvalidNickname {})?;
2300
2301            if tiny.is_ascii_alphanumeric() && !tiny.is_empty() {
2302                Ok(Nickname(tiny))
2303            } else {
2304                Err(InvalidNickname {})
2305            }
2306        }
2307    }
2308
2309    impl crate::NormalItemArgument for Nickname {}
2310}
2311
2312/// Hostnames etc.
2313//
2314// TODO maybe move all this to tor-basic-utils
2315mod hostname {
2316    use super::*;
2317    use std::net::IpAddr;
2318
2319    /// Internet hostname
2320    ///
2321    /// Valid according to Internet RFC1123,
2322    /// with the additional restriction that there must be at least one letter.
2323    /// (That means that anything accepted as a `Hostname`
2324    /// won't be accepted as an address even by very relaxed IPv4 address parsers.
2325    /// We presume that no TLD will ever exist that is entirely decimal digits.)
2326    ///
2327    /// Preserves case.
2328    ///
2329    /// Reserved hostname such as `example.come`, `tor.invalid` and `localhost`
2330    /// are accepted.
2331    ///
2332    /// # Comparisons; `PartialEq`, `Eq`
2333    ///
2334    /// The `PartialEq` and `Eq` implementations are case sensitive,
2335    /// even though internet hostnames are not case-sensitive.
2336    ///
2337    /// Comparing hostnames for identical apparent meaning is complicated.
2338    /// If you need to do that, you (may) need to engage with punycode (IDN),
2339    /// as well as arranging for a case-insensitive comparison.
2340    ///
2341    /// And of course, hostnames reference to the DNS.
2342    /// A single host may have multiple names and it may change its address.
2343    #[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] //
2344    #[derive(derive_more::Into, derive_more::Deref, derive_more::AsRef, derive_more::Display)]
2345    pub struct Hostname(String);
2346
2347    /// Hostname, or IP address (v4 or v6)
2348    ///
2349    /// Preserves hostname case.  See [`Hostname`].
2350    ///
2351    /// Reserved hostnames and addresses (eg `0.0.0.0` or `tor.invalid`) are accepted.
2352    ///
2353    /// IPv6 addresses are represented *without* surrounding `[ ]`.
2354    ///
2355    /// Therefore, you cannot make this into a host-and-port by appending `:port`.
2356    /// To process name-and-port is complex.  `SRV` (or `MX`) records might be involved.
2357    //
2358    // This type is called `InternetHost` to emphasise that it is primarily for
2359    // hosts on the public internet and, unlike arti-client's `Host`,
2360    // has special handling of `.onion` addresses.
2361    #[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] //
2362    #[derive(derive_more::Display)]
2363    #[allow(clippy::exhaustive_enums)]
2364    // TODO derive .as_hostname(), .as_ip_addr(), From<Hostname>, From<IpAddr>
2365    pub enum InternetHost {
2366        /// Hostname
2367        #[display("{_0}")]
2368        Name(Hostname),
2369        /// IP address (v4 or v6)
2370        #[display("{_0}")]
2371        IpAddr(IpAddr),
2372    }
2373
2374    /// Invalid hostname
2375    #[derive(Clone, Debug, thiserror::Error)]
2376    #[error("invalid hostname")]
2377    #[non_exhaustive]
2378    pub struct InvalidHostname {}
2379
2380    /// Invalid Internet hostname/address
2381    #[derive(Clone, Debug, thiserror::Error)]
2382    #[error("invalid: not a valid hostname, nor a valid IPv4 or IPv6 address")]
2383    #[non_exhaustive]
2384    pub struct InvalidInternetHost {}
2385
2386    impl Hostname {
2387        /// Obtain this hostname as a `str`
2388        pub fn as_str(&self) -> &str {
2389            &self.0
2390        }
2391    }
2392
2393    impl AsRef<str> for Hostname {
2394        fn as_ref(&self) -> &str {
2395            self.as_str()
2396        }
2397    }
2398
2399    impl TryFrom<String> for Hostname {
2400        type Error = InvalidHostname;
2401        fn try_from(s: String) -> Result<Self, InvalidHostname> {
2402            if hostname_validator::is_valid(&s) &&
2403                // Reject hostnames that consist only of decimal digits and full stops.
2404                // Some of those are accepted by some old IPv4 address parsers.
2405                // If any fool makes a TLD that is only digits, they deserve everything they get.
2406                !s.chars().all(|c| c.is_ascii_digit() || c == '.')
2407            {
2408                Ok(Hostname(s))
2409            } else {
2410                Err(InvalidHostname {})
2411            }
2412        }
2413    }
2414
2415    impl FromStr for Hostname {
2416        type Err = InvalidHostname;
2417        fn from_str(s: &str) -> Result<Self, InvalidHostname> {
2418            s.to_owned().try_into()
2419        }
2420    }
2421
2422    impl FromStr for InternetHost {
2423        type Err = InvalidInternetHost;
2424        fn from_str(s: &str) -> Result<Self, InvalidInternetHost> {
2425            if let Ok(y) = s.parse() {
2426                Ok(InternetHost::IpAddr(y))
2427            } else if let Ok(y) = s.parse() {
2428                Ok(InternetHost::Name(y))
2429            } else {
2430                // For simplicity, we  discard the errors from parsing the options
2431                // rather than trying to reproduce them.  Why something isn't a valid
2432                // address or hostname ought to be fairly obvious.
2433                Err(InvalidInternetHost {})
2434            }
2435        }
2436    }
2437
2438    impl NormalItemArgument for Hostname {}
2439    impl NormalItemArgument for InternetHost {}
2440}
2441
2442/// Contact information of the relay operator.
2443mod contact_info {
2444    use super::*;
2445
2446    /// `contact` item: contact information (eg of a relay dirauth operator)
2447    ///
2448    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:contact>
2449    ///
2450    /// Also used for authority entries in netstatus documents.
2451    #[derive(Clone, Debug, PartialEq, Eq, Deftly)] //
2452    #[derive(derive_more::Into, derive_more::AsRef, derive_more::Deref, derive_more::Display)]
2453    #[derive_deftly(ItemValueEncodable)]
2454    #[non_exhaustive]
2455    pub struct ContactInfo(#[deftly(netdoc(rest))] String);
2456
2457    /// Contact information (`contact` item value) has invalid syntax
2458    #[derive(Clone, Debug, thiserror::Error)]
2459    #[error("contact information (`contact` item value) has invalid syntax")]
2460    #[non_exhaustive]
2461    pub struct InvalidContactInfo {}
2462
2463    impl FromStr for ContactInfo {
2464        type Err = InvalidContactInfo;
2465
2466        fn from_str(s: &str) -> Result<Self, InvalidContactInfo> {
2467            // TODO torspec#396 we should probably impose more restrictions
2468            // For now we forbid `\n` and initial whitespace, which is enough to ensure
2469            // that all values will roundtrip unchanged through netdoc encoding and parsing.
2470            if s.contains('\n') || s.starts_with(char::is_whitespace) {
2471                Err(InvalidContactInfo {})
2472            } else {
2473                Ok(ContactInfo(s.to_owned()))
2474            }
2475        }
2476    }
2477
2478    impl ItemValueParseable for ContactInfo {
2479        fn from_unparsed(mut item: UnparsedItem<'_>) -> Result<Self, parse2::ErrorProblem> {
2480            item.check_no_object()?;
2481            item.args_mut()
2482                .into_remaining()
2483                .parse()
2484                .map_err(|_e| item.args().handle_error("info", ArgumentError::Invalid))
2485        }
2486    }
2487}
2488
2489/// Types for boolean-like types.
2490mod boolean {
2491    use derive_deftly::Deftly;
2492    use std::{
2493        fmt::Display,
2494        ops::{Deref, DerefMut},
2495        str::FromStr,
2496    };
2497
2498    use crate::{Error, NetdocErrorKind as EK, NormalItemArgument, Pos};
2499
2500    /// A boolean that is represented by a `0` (false) or `1` (true).
2501    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Deftly)]
2502    #[derive_deftly(Transparent)]
2503    #[allow(clippy::exhaustive_structs)]
2504    pub struct NumericBoolean(pub bool);
2505
2506    impl FromStr for NumericBoolean {
2507        type Err = Error;
2508
2509        fn from_str(s: &str) -> Result<Self, Self::Err> {
2510            match s {
2511                "0" => Ok(Self(false)),
2512                "1" => Ok(Self(true)),
2513                _ => Err(EK::BadArgument
2514                    .at_pos(Pos::at(s))
2515                    .with_msg("Invalid numeric boolean")),
2516            }
2517        }
2518    }
2519
2520    impl Display for NumericBoolean {
2521        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2522            write!(f, "{}", u8::from(self.0))
2523        }
2524    }
2525
2526    impl NormalItemArgument for NumericBoolean {}
2527}
2528
2529/// Types for router descriptors.
2530pub mod routerdesc {
2531    use crate::types::EmbeddedCert;
2532
2533    use super::*;
2534    use parse2::ErrorProblem as EP;
2535    use tor_cert::KeyUnknownCert;
2536    use tor_llcrypto::pk::ed25519;
2537
2538    /// Version argument found in an `overload-general` item.
2539    ///
2540    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:overload-general>
2541    #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, strum::EnumString, strum::Display)]
2542    #[non_exhaustive]
2543    pub enum OverloadGeneralVersion {
2544        /// Version 1, currently the only supported and specified one.
2545        #[strum(serialize = "1")]
2546        V1,
2547    }
2548
2549    impl NormalItemArgument for OverloadGeneralVersion {}
2550
2551    /// The overload general type found in router descriptors.
2552    ///
2553    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:overload-general>
2554    #[derive(Debug, Clone, Copy, PartialEq, Eq, Deftly)]
2555    #[derive_deftly(ItemValueParseable, ItemValueEncodable)]
2556    #[non_exhaustive]
2557    pub struct OverloadGeneral {
2558        /// The version of the item.
2559        pub version: OverloadGeneralVersion,
2560        /// The timestamp since when the relay is overloaded.
2561        pub since: Iso8601TimeSp,
2562    }
2563
2564    /// Introduction line of a router descriptor.
2565    ///
2566    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:router>
2567    #[derive(Clone, Debug, PartialEq, Eq, Deftly)]
2568    #[derive_deftly(ItemValueParseable, ItemValueEncodable)]
2569    #[non_exhaustive]
2570    pub struct RouterDescIntroItem {
2571        /// A valid router [`Nickname`].
2572        pub nickname: Nickname,
2573
2574        /// An IPv4 address in dotted-squad format.
2575        pub address: std::net::Ipv4Addr,
2576
2577        /// The TCP port of the onion router.
2578        pub orport: u16,
2579
2580        /// Legacy.
2581        pub socksport: u16,
2582
2583        /// Legacy.
2584        pub dirport: u16,
2585    }
2586
2587    /// Digest identifying the extra-info document.
2588    ///
2589    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:extra-info-digest>
2590    #[derive(Clone, Debug, PartialEq, Eq, Deftly)]
2591    #[derive_deftly(ItemValueParseable, ItemValueEncodable)]
2592    #[non_exhaustive]
2593    pub struct ExtraInfoDigests {
2594        /// Mandatory SHA-1 of the signed data in base 16.
2595        pub sha1: FixedB16U<20>,
2596
2597        /// Optional SHA-256 of the entire extra-info in base 64.
2598        pub sha2: Option<FixedB64<32>>,
2599    }
2600
2601    /// Accumulator for router descriptor hash signatures.
2602    #[derive(Debug, Clone, Default, Deftly)]
2603    #[derive_deftly(AsMutSelf)]
2604    #[allow(clippy::exhaustive_structs)]
2605    pub struct RouterHashAccu {
2606        /// Potentially the SHA-1 for the signature.
2607        pub sha1: Option<[u8; 20]>,
2608        /// Potentially the SHA-256 for the signature.
2609        pub sha256: Option<[u8; 32]>,
2610    }
2611
2612    /// SHA-256 router descriptor signature including magic and the keyword.
2613    #[derive(Debug, Clone, PartialEq, Eq, Deftly)]
2614    #[derive_deftly(ItemValueEncodable)]
2615    #[allow(clippy::exhaustive_structs)]
2616    // TODO SPEC is RouterSigEd25519 not a standard-ish kind of signature?
2617    // TODO DIRAUTH is RouterSigEd25519 not a standard-ish kind of signature?
2618    pub struct RouterSigEd25519(pub ed25519::Signature);
2619
2620    impl RouterSigEd25519 {
2621        /// The magic prefix for hashing this type of signature.
2622        const HASH_PREFIX_MAGIC: &str = "Tor router descriptor signature v1";
2623
2624        /// Calculate the hash for signature
2625        ///
2626        /// `signature_item_kw_spc` is the keyword *with a trailing space*.
2627        /// It's `&[&str]` for the convenience of the two call sites.
2628        fn hash(document_sofar: &str, signature_item_kw_spc: &[&str]) -> [u8; 32] {
2629            debug_assert!(
2630                signature_item_kw_spc
2631                    .last()
2632                    .expect("signature_item_kw_spc")
2633                    .ends_with(" ")
2634            );
2635            let mut h = tor_llcrypto::d::Sha256::new();
2636            h.update(Self::HASH_PREFIX_MAGIC);
2637            h.update(document_sofar);
2638            for b in signature_item_kw_spc {
2639                h.update(b);
2640            }
2641            h.finalize().into()
2642        }
2643
2644        /// Make a signature during document encoding
2645        ///
2646        /// `item_keyword` is the keyword for the signature item.
2647        ///
2648        /// # Example
2649        ///
2650        /// ```
2651        /// use derive_deftly::Deftly;
2652        /// use tor_error::Bug;
2653        /// use tor_llcrypto::pk::ed25519;
2654        /// use tor_netdoc::derive_deftly_template_NetdocEncodable;
2655        /// use tor_netdoc::encode::{NetdocEncodable, NetdocEncoder};
2656        /// use tor_netdoc::types::routerdesc::RouterSigEd25519;
2657        ///
2658        /// #[derive(Deftly, Default)]
2659        /// #[derive_deftly(NetdocEncodable)]
2660        /// pub struct Document {
2661        ///     pub document_intro_keyword: (),
2662        /// }
2663        /// #[derive(Deftly)]
2664        /// #[derive_deftly(NetdocEncodable)]
2665        /// pub struct DocumentSignatures {
2666        ///     pub document_signature: RouterSigEd25519,
2667        /// }
2668        /// impl Document {
2669        ///     pub fn encode_sign(&self, k: &ed25519::Keypair) -> Result<String, Bug> {
2670        ///         let mut encoder = NetdocEncoder::new();
2671        ///         self.encode_unsigned(&mut encoder)?;
2672        ///         let document_signature =
2673        ///             RouterSigEd25519::new_sign_netdoc(k, &encoder, "document-signature")?;
2674        ///         let sigs = DocumentSignatures { document_signature };
2675        ///         sigs.encode_unsigned(&mut encoder)?;
2676        ///         let encoded = encoder.finish()?;
2677        ///         Ok(encoded)
2678        ///     }
2679        /// }
2680        ///
2681        /// # fn main() -> Result<(), anyhow::Error> {
2682        /// let k = ed25519::Keypair::generate(&mut tor_basic_utils::test_rng::testing_rng());
2683        /// let doc = Document::default();
2684        /// let encoded = doc.encode_sign(&k)?;
2685        /// assert!(encoded.starts_with(concat!(
2686        ///     "document-intro-keyword\n",
2687        ///     "document-signature ",
2688        /// )));
2689        /// # Ok(())
2690        /// # }
2691        /// ```
2692        pub fn new_sign_netdoc(
2693            private_key: &ed25519::Keypair,
2694            encoder: &NetdocEncoder,
2695            item_keyword: &str,
2696        ) -> StdResult<Self, Bug> {
2697            let signature = private_key
2698                .sign(&Self::hash(encoder.text_sofar()?, &[item_keyword, " "]))
2699                .to_bytes()
2700                .into();
2701            Ok(RouterSigEd25519(signature))
2702        }
2703    }
2704
2705    impl SignatureItemParseable for RouterSigEd25519 {
2706        type HashAccu = RouterHashAccu;
2707
2708        fn from_unparsed_and_body(
2709            mut item: UnparsedItem<'_>,
2710            hash_inputs: &SignatureHashInputs<'_>,
2711            hash: &mut Self::HashAccu,
2712        ) -> Result<Self, EP> {
2713            // TODO DIRMIRROR break this out into impl ItemArgumentParseable for Signature
2714            let args = item.args_mut();
2715            let sig = FixedB64::<64>::from_args(args)
2716                .map_err(|e| args.handle_error("router-sig-ed25519", e))?
2717                .0;
2718            let sig = ed25519::Signature::from(sig);
2719            hash.sha256 = Some(Self::hash(
2720                hash_inputs.document_sofar,
2721                &[hash_inputs.signature_item_kw_spc],
2722            ));
2723            Ok(Self(sig))
2724        }
2725    }
2726
2727    /// SHA-1 router descriptor signature over `router-sig-ed25519`.
2728    // TODO DIRMIRROR Is this not the same as RsaSha1Signature ?
2729    #[derive(Debug, Clone, PartialEq, Eq)]
2730    #[allow(clippy::exhaustive_structs)]
2731    pub struct RouterSignature(pub Vec<u8>);
2732
2733    impl SignatureItemParseable for RouterSignature {
2734        type HashAccu = RouterHashAccu;
2735
2736        fn from_unparsed_and_body(
2737            mut item: UnparsedItem<'_>,
2738            hash_inputs: &SignatureHashInputs<'_>,
2739            hash: &mut Self::HashAccu,
2740        ) -> Result<Self, EP> {
2741            // There must be no additonal arguments.
2742            let args = item.args_mut();
2743            if args.next().is_some() {
2744                return Err(EP::UnexpectedArgument {
2745                    column: args.prev_arg_column(),
2746                });
2747            }
2748            let obj = item.object().ok_or(EP::MissingObject)?.decode_data()?;
2749
2750            let mut h = tor_llcrypto::d::Sha1::new();
2751            h.update(hash_inputs.document_sofar);
2752            h.update(hash_inputs.signature_item_line);
2753            h.update("\n");
2754            hash.sha1 = Some(h.finalize().into());
2755
2756            Ok(Self(obj))
2757        }
2758    }
2759
2760    /// Estimated bandwidth for a router.
2761    ///
2762    /// <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:bandwidth>
2763    // Does not derive Ord because it only makes sense to order on a single
2764    // field but not all.
2765    #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Deftly)]
2766    #[derive_deftly(ItemValueParseable, ItemValueEncodable)]
2767    #[non_exhaustive]
2768    pub struct Bandwidth {
2769        /// The volume that the relay is willing to sustain over long periods.
2770        pub average: u64,
2771
2772        /// The volume that the relay is willing to sustain in very short intervals.
2773        pub burst: u64,
2774
2775        /// The estimate of the capacity this relay can handle.
2776        pub observed: u64,
2777    }
2778
2779    /// Ntor onion key cross-certificate.
2780    ///
2781    /// This struct contains an [`Ed25519NtorCrossCert`] alongside the `bit`
2782    /// field required for converting the ntor X25519 key to an Ed25519 key.
2783    ///
2784    /// # See Also
2785    ///
2786    /// * [`Ed25519NtorCrossCert`]
2787    /// * <https://spec.torproject.org/dir-spec/server-descriptor-format.html#item:ntor-onion-key-crosscert>
2788    #[derive(Debug, Clone, Deftly, PartialEq, Eq)]
2789    #[derive_deftly(ItemValueParseable, ItemValueEncodable)]
2790    #[deftly(netdoc(no_extra_args))]
2791    #[non_exhaustive]
2792    pub struct NtorOnionKeyCrossCert {
2793        /// True if X coordinate of the ntor onion key is negative, false if
2794        /// positive.
2795        // TODO spec: This name is very unfortunate, how about we change it
2796        // to `is_negative`.  Also, using a boolean for storing a sign bit feels
2797        // wrong to me due to the zero edge case, which would not be negative,
2798        // but also not positive either.
2799        pub bit: NumericBoolean,
2800
2801        /// The actual embedded ntor onion key certificate.
2802        #[deftly(netdoc(object))]
2803        pub cert: EmbeddedCert<Ed25519NtorCrossCert, KeyUnknownCert>,
2804    }
2805}
2806
2807#[cfg(test)]
2808mod test {
2809    // @@ begin test lint list maintained by maint/add_warning @@
2810    #![allow(clippy::bool_assert_comparison)]
2811    #![allow(clippy::clone_on_copy)]
2812    #![allow(clippy::dbg_macro)]
2813    #![allow(clippy::mixed_attributes_style)]
2814    #![allow(clippy::print_stderr)]
2815    #![allow(clippy::print_stdout)]
2816    #![allow(clippy::single_char_pattern)]
2817    #![allow(clippy::unwrap_used)]
2818    #![allow(clippy::unchecked_time_subtraction)]
2819    #![allow(clippy::useless_vec)]
2820    #![allow(clippy::needless_pass_by_value)]
2821    #![allow(clippy::string_slice)] // See arti#2571
2822    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
2823    use std::{
2824        fmt::Debug,
2825        time::{Duration, SystemTime},
2826    };
2827
2828    use itertools::Itertools;
2829
2830    use base64ct::Encoding;
2831    use tor_basic_utils::test_rng::testing_rng;
2832    use tor_cert::{CertType, CertifiedKey, Ed25519Cert, KeyUnknownCert};
2833    use tor_checkable::{Timebound, timed::TimerangeBound};
2834    use tor_llcrypto::pk::ed25519::{self, Ed25519Identity, Ed25519PublicKey, ExpandedKeypair};
2835
2836    use super::*;
2837    use crate::{
2838        Pos, Result,
2839        encode::NetdocEncodable,
2840        parse2::{ErrorProblem, ParseInput, VerifyFailed},
2841        types::{
2842            EmbeddedCert,
2843            routerdesc::{NtorOnionKeyCrossCert, RouterDescIntroItem},
2844        },
2845    };
2846
2847    /// Decode s as a multi-line base64 string, ignoring ascii whitespace.
2848    fn base64_decode_ignore_ws(s: &str) -> std::result::Result<Vec<u8>, base64ct::Error> {
2849        let mut s = s.to_string();
2850        s.retain(|c| !c.is_ascii_whitespace());
2851        base64ct::Base64::decode_vec(s.as_str())
2852    }
2853
2854    #[test]
2855    fn base64() -> Result<()> {
2856        // Test parsing success:
2857        // Unpadded:
2858        assert_eq!("Mi43MTgyOA".parse::<B64>()?.as_bytes(), &b"2.71828"[..]);
2859        assert!("Mi43MTgyOA".parse::<B64>()?.check_len(7..8).is_ok());
2860        assert_eq!("Mg".parse::<B64>()?.as_bytes(), &b"2"[..]);
2861        assert!("Mg".parse::<B64>()?.check_len(1..2).is_ok());
2862        assert_eq!(
2863            "8J+NkvCfjZLwn42S8J+NkvCfjZLwn42S"
2864                .parse::<B64>()?
2865                .as_bytes(),
2866            "🍒🍒🍒🍒🍒🍒".as_bytes()
2867        );
2868        assert!(
2869            "8J+NkvCfjZLwn42S8J+NkvCfjZLwn42S"
2870                .parse::<B64>()?
2871                .check_len(24..25)
2872                .is_ok()
2873        );
2874        assert!(
2875            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz8="
2876                .parse::<B64>()?
2877                .check_len(32..33)
2878                .is_ok()
2879        );
2880        // Padded:
2881        assert_eq!("Mi43MTgyOA==".parse::<B64>()?.as_bytes(), &b"2.71828"[..]);
2882        assert!("Mi43MTgyOA==".parse::<B64>()?.check_len(7..8).is_ok());
2883        assert_eq!("Mg==".parse::<B64>()?.as_bytes(), &b"2"[..]);
2884        assert!("Mg==".parse::<B64>()?.check_len(1..2).is_ok());
2885
2886        // Test parsing failures:
2887        // Invalid character.
2888        assert!("Mi43!!!!!!".parse::<B64>().is_err());
2889        // Invalid last character.
2890        assert!("Mi".parse::<B64>().is_err());
2891        assert!(
2892            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxaaaa"
2893                .parse::<B64>()
2894                .is_err()
2895        );
2896        // Invalid length.
2897        assert!("Mi43MTgyOA".parse::<B64>()?.check_len(8..).is_err());
2898        Ok(())
2899    }
2900
2901    #[test]
2902    fn base64_lengths() -> Result<()> {
2903        assert_eq!("".parse::<B64>()?.as_bytes(), b"");
2904        assert!("=".parse::<B64>().is_err());
2905        assert!("==".parse::<B64>().is_err());
2906        assert!("B".parse::<B64>().is_err());
2907        assert!("B=".parse::<B64>().is_err());
2908        assert!("B==".parse::<B64>().is_err());
2909        assert!("Bg=".parse::<B64>().is_err());
2910        assert_eq!("Bg".parse::<B64>()?.as_bytes(), b"\x06");
2911        assert_eq!("Bg==".parse::<B64>()?.as_bytes(), b"\x06");
2912        assert_eq!("BCg".parse::<B64>()?.as_bytes(), b"\x04\x28");
2913        assert_eq!("BCg=".parse::<B64>()?.as_bytes(), b"\x04\x28");
2914        assert!("BCg==".parse::<B64>().is_err());
2915        assert_eq!("BCDE".parse::<B64>()?.as_bytes(), b"\x04\x20\xc4");
2916        assert!("BCDE=".parse::<B64>().is_err());
2917        assert!("BCDE==".parse::<B64>().is_err());
2918        Ok(())
2919    }
2920
2921    #[test]
2922    fn base64_rev() {
2923        use base64ct::{Base64, Base64Unpadded};
2924
2925        // Check that strings that we accept are precisely ones which
2926        // can be generated by either Base64 or Base64Unpadded
2927        for n in 0..=5 {
2928            for c_vec in std::iter::repeat_n("ACEQg/=".chars(), n).multi_cartesian_product() {
2929                let s: String = c_vec.into_iter().collect();
2930                #[allow(clippy::print_stderr)]
2931                let b = match s.parse::<B64>() {
2932                    Ok(b) => {
2933                        eprintln!("{:10} {:?}", &s, b.as_bytes());
2934                        b
2935                    }
2936                    Err(_) => {
2937                        eprintln!("{:10} Err", &s);
2938                        continue;
2939                    }
2940                };
2941                let b = b.as_bytes();
2942
2943                let ep = Base64::encode_string(b);
2944                let eu = Base64Unpadded::encode_string(b);
2945
2946                assert!(
2947                    s == ep || s == eu,
2948                    "{:?} decoded to {:?} giving neither {:?} nor {:?}",
2949                    s,
2950                    b,
2951                    ep,
2952                    eu
2953                );
2954            }
2955        }
2956    }
2957
2958    #[test]
2959    fn base16() -> anyhow::Result<()> {
2960        let chk = |s: &str, b: &[u8]| -> anyhow::Result<()> {
2961            let parsed = s.parse::<B16>()?;
2962            assert_eq!(parsed.as_bytes(), b, "{s:?}");
2963            assert_eq!(parsed.to_string(), s.to_ascii_lowercase());
2964
2965            let parsed = s.parse::<B16U>()?;
2966            assert_eq!(parsed.as_bytes(), b, "{s:?}");
2967            assert_eq!(parsed.to_string(), s.to_ascii_uppercase());
2968            Ok(())
2969        };
2970
2971        chk("332e313432", b"3.142")?;
2972        chk("332E313432", b"3.142")?;
2973        chk("332E3134", b"3.14")?;
2974
2975        assert!("332E313".parse::<B16>().is_err());
2976        assert!("332G3134".parse::<B16>().is_err());
2977        Ok(())
2978    }
2979
2980    #[test]
2981    fn curve25519() -> Result<()> {
2982        use tor_llcrypto::pk::curve25519::PublicKey;
2983        let k1 = "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz8=";
2984        let k2 = hex::decode("a69c2d8475d6f245c3d1ff5f13b50f62c38002ee2e8f9391c12a2608cc4a933f")
2985            .unwrap();
2986        let k2: &[u8; 32] = &k2[..].try_into().unwrap();
2987
2988        let k1: PublicKey = k1.parse::<Curve25519Public>()?.into();
2989        assert_eq!(k1, (*k2).into());
2990
2991        assert!(
2992            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz"
2993                .parse::<Curve25519Public>()
2994                .is_err()
2995        );
2996        assert!(
2997            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORSomCMxKkz"
2998                .parse::<Curve25519Public>()
2999                .is_err()
3000        );
3001        assert!(
3002            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5wSomCMxKkz"
3003                .parse::<Curve25519Public>()
3004                .is_err()
3005        );
3006        assert!(
3007            "ppwthHXW8kXD0f9fE7UPYsOAAu4ORwSomCMxKkz"
3008                .parse::<Curve25519Public>()
3009                .is_err()
3010        );
3011
3012        Ok(())
3013    }
3014
3015    #[test]
3016    fn ed25519() -> Result<()> {
3017        use tor_llcrypto::pk::ed25519::Ed25519Identity;
3018        let k1 = "WVIPQ8oArAqLY4XzkcpIOI6U8KsUJHBQhG8SC57qru0";
3019        let k2 = hex::decode("59520f43ca00ac0a8b6385f391ca48388e94f0ab14247050846f120b9eeaaeed")
3020            .unwrap();
3021
3022        let k1: Ed25519Identity = k1.parse::<Ed25519Public>()?.into();
3023        assert_eq!(k1, Ed25519Identity::from_bytes(&k2).unwrap());
3024
3025        assert!(
3026            "WVIPQ8oArAqLY4Xzk0!!!!8KsUJHBQhG8SC57qru"
3027                .parse::<Ed25519Public>()
3028                .is_err()
3029        );
3030        assert!(
3031            "WVIPQ8oArAqLY4XzkcpIU8KsUJHBQhG8SC57qru"
3032                .parse::<Ed25519Public>()
3033                .is_err()
3034        );
3035        assert!(
3036            "WVIPQ8oArAqLY4XzkcpIU8KsUJHBQhG8SC57qr"
3037                .parse::<Ed25519Public>()
3038                .is_err()
3039        );
3040        // right length, bad key:
3041        assert!(
3042            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxaaaa"
3043                .parse::<Curve25519Public>()
3044                .is_err()
3045        );
3046        Ok(())
3047    }
3048
3049    #[test]
3050    fn time() -> Result<()> {
3051        use humantime::parse_rfc3339;
3052        use std::time::SystemTime;
3053
3054        let t = "2020-09-29 13:36:33".parse::<Iso8601TimeSp>()?;
3055        let t: SystemTime = t.into();
3056        assert_eq!(t, parse_rfc3339("2020-09-29T13:36:33Z").unwrap());
3057
3058        assert!("2020-FF-29 13:36:33".parse::<Iso8601TimeSp>().is_err());
3059        assert!("2020-09-29Q13:99:33".parse::<Iso8601TimeSp>().is_err());
3060        assert!("2020-09-29".parse::<Iso8601TimeSp>().is_err());
3061        assert!("too bad, waluigi time".parse::<Iso8601TimeSp>().is_err());
3062
3063        assert_eq!(
3064            "2020-09-29 13:36:33",
3065            "2020-09-29 13:36:33".parse::<Iso8601TimeSp>()?.to_string()
3066        );
3067
3068        let t = "2020-09-29T13:36:33".parse::<Iso8601TimeNoSp>()?;
3069        let t: SystemTime = t.into();
3070        assert_eq!(t, parse_rfc3339("2020-09-29T13:36:33Z").unwrap());
3071
3072        assert!("2020-09-29 13:36:33".parse::<Iso8601TimeNoSp>().is_err());
3073        assert!("2020-09-29Q13:99:33".parse::<Iso8601TimeNoSp>().is_err());
3074        assert!("2020-09-29".parse::<Iso8601TimeNoSp>().is_err());
3075        assert!("too bad, waluigi time".parse::<Iso8601TimeNoSp>().is_err());
3076
3077        assert_eq!(
3078            "2020-09-29T13:36:33",
3079            "2020-09-29T13:36:33"
3080                .parse::<Iso8601TimeNoSp>()?
3081                .to_string()
3082        );
3083
3084        Ok(())
3085    }
3086
3087    #[test]
3088    fn rsa_public_key() {
3089        // Taken from a chutney network.
3090        let key_b64 = r#"
3091        MIIBigKCAYEAsDkzTcKS4kAF56R2ijb9qCek53tKC1EwMdpWMk58bB28fY6kHc55
3092        E7n1hB+LC5neZlx88GKuZ9k8P3g0MlO5ejalcfBdIIm28Nz86JXf/L23YnEpxnG/
3093        IpxZEcmx/EYN+vwp72W3DGuzyntaoaut6lGJk+O/aRCLLcTm4MNznvN1ackK2H6b
3094        Xm2ejRwtVRLoPKODJiPGl43snCfXXWsMH3IALFOgm0szPLv2fAJzBI8VWrUN81M/
3095        lgwJhG6+xbr1CkrXI5fKs/TNr0B0ydC9BIZplmPrnXaeNklnw1cqUJ1oxDSgBrvx
3096        rpDo7paObjSPV26opa68QKGa7Gu2MZQC3RzViNCbawka/108g6hSUkoM+Om2oivr
3097        DvtMOs10MjsfibEBVnwEhqnlb/gj3hJkYoGRsCwAyMIaMObHcmAevMJRWAjGCc8T
3098        GMS9dSmg1IZst+U+V2OCcIHXT6wZ1zPsBM0pYKVLCwtewaq1306k0n+ekriEo7eI
3099        FS3Dd/Dx/a6jAgMBAAE=
3100        "#;
3101        let key_bytes = base64_decode_ignore_ws(key_b64).unwrap();
3102        let rsa = RsaPublicParse1Helper::from_vec(key_bytes, Pos::None).unwrap();
3103
3104        let bits = tor_llcrypto::pk::rsa::PublicKey::from(rsa.clone()).bits();
3105        assert_eq!(bits, 3072);
3106
3107        // tests on a valid key
3108        assert!(rsa.clone().check_exponent(65537).is_ok());
3109        assert!(rsa.clone().check_exponent(1337).is_err());
3110        assert!(rsa.clone().check_len_eq(3072).is_ok());
3111        assert!(rsa.clone().check_len(1024..=4096).is_ok());
3112        assert!(rsa.clone().check_len(1024..=1024).is_err());
3113        assert!(rsa.check_len(4096..).is_err());
3114
3115        // A string of bytes that is not an RSA key.
3116        let failure = RsaPublicParse1Helper::from_vec(vec![1, 2, 3], Pos::None);
3117        assert!(failure.is_err());
3118    }
3119
3120    #[test]
3121    fn ed_cert() {
3122        use tor_llcrypto::pk::ed25519::Ed25519Identity;
3123
3124        // From a chutney network.
3125        let cert_b64 = r#"
3126        AQQABwRNAR6m3kq5h8i3wwac+Ti293opoOP8RKGP9MT0WD4Bbz7YAQAgBACGCdys
3127        G7AwsoYMIKenDN6In6ReiGF8jaYoGqmWKDVBdGGMDIZyNIq+VdhgtAB1EyNFHJU1
3128        jGM0ir9dackL+PIsHbzJH8s/P/8RfUsKIL6/ZHbn3nKMxLH/8kjtxp5ScAA=
3129        "#;
3130        let cert_bytes = base64_decode_ignore_ws(cert_b64).unwrap();
3131        // From the cert above.
3132        let right_subject_key: Ed25519Identity = "HqbeSrmHyLfDBpz5OLb3eimg4/xEoY/0xPRYPgFvPtg"
3133            .parse::<Ed25519Public>()
3134            .unwrap()
3135            .into();
3136        // From `ed25519()` test above.
3137        let wrong_subject_key: Ed25519Identity = "WVIPQ8oArAqLY4XzkcpIOI6U8KsUJHBQhG8SC57qru0"
3138            .parse::<Ed25519Public>()
3139            .unwrap()
3140            .into();
3141
3142        // decode and check correct type and key
3143        let cert = UnvalidatedEdCert::from_vec(cert_bytes, Pos::None)
3144            .unwrap()
3145            .check_cert_type(tor_cert::CertType::IDENTITY_V_SIGNING)
3146            .unwrap()
3147            .check_subject_key_is(&right_subject_key)
3148            .unwrap();
3149        // check wrong type.
3150        assert!(
3151            cert.clone()
3152                .check_cert_type(tor_cert::CertType::RSA_ID_X509)
3153                .is_err()
3154        );
3155        // check wrong key.
3156        assert!(cert.check_subject_key_is(&wrong_subject_key).is_err());
3157
3158        // Try an invalid object that isn't a certificate.
3159        let failure = UnvalidatedEdCert::from_vec(vec![1, 2, 3], Pos::None);
3160        assert!(failure.is_err());
3161    }
3162
3163    #[test]
3164    fn fingerprint() -> Result<()> {
3165        use tor_llcrypto::pk::rsa::RsaIdentity;
3166        let fp1 = "7467 A97D 19CD 2B4F 2BC0 388A A99C 5E67 710F 847E";
3167        let fp2 = "7467A97D19CD2B4F2BC0388AA99C5E67710F847E";
3168        let fp3 = "$7467A97D19CD2B4F2BC0388AA99C5E67710F847E";
3169        let fp4 = "$7467A97D19CD2B4F2BC0388AA99C5E67710F847E=fred";
3170
3171        let k = hex::decode(fp2).unwrap();
3172        let k = RsaIdentity::from_bytes(&k[..]).unwrap();
3173
3174        assert_eq!(RsaIdentity::from(fp1.parse::<SpFingerprint>()?), k);
3175        assert_eq!(RsaIdentity::from(fp2.parse::<SpFingerprint>()?), k);
3176        assert!(fp3.parse::<SpFingerprint>().is_err());
3177        assert!(fp4.parse::<SpFingerprint>().is_err());
3178
3179        assert!(fp1.parse::<Fingerprint>().is_err());
3180        assert_eq!(RsaIdentity::from(fp2.parse::<Fingerprint>()?), k);
3181        assert!(fp3.parse::<Fingerprint>().is_err());
3182        assert!(fp4.parse::<Fingerprint>().is_err());
3183        assert_eq!(Fingerprint(k).to_string(), fp2);
3184
3185        assert!(fp1.parse::<LongIdent>().is_err());
3186        assert_eq!(RsaIdentity::from(fp2.parse::<LongIdent>()?), k);
3187        assert_eq!(RsaIdentity::from(fp3.parse::<LongIdent>()?), k);
3188        assert_eq!(RsaIdentity::from(fp4.parse::<LongIdent>()?), k);
3189
3190        assert!("xxxx".parse::<Fingerprint>().is_err());
3191        assert!("ffffffffff".parse::<Fingerprint>().is_err());
3192
3193        let fp_b64 = "dGepfRnNK08rwDiKqZxeZ3EPhH4";
3194        assert_eq!(RsaIdentity::from(fp_b64.parse::<Base64Fingerprint>()?), k);
3195        assert_eq!(Base64Fingerprint(k).to_string(), fp_b64);
3196
3197        Ok(())
3198    }
3199
3200    #[test]
3201    fn nickname() -> anyhow::Result<()> {
3202        let n: Nickname = "Foo".parse()?;
3203        assert_eq!(n.as_str(), "Foo");
3204        assert_eq!(n.to_string(), "Foo");
3205
3206        let word = "Untr1gonometr1cally";
3207        assert_eq!(word.len(), 19);
3208        let long: Nickname = word.parse()?;
3209        assert_eq!(long.as_str(), word);
3210
3211        let too_long = "abcdefghijklmnopqrstuvwxyz";
3212        let not_ascii = "Eyjafjallajökull";
3213        let too_short = "";
3214        let other_invalid = "contains space";
3215        assert!(not_ascii.len() <= 19);
3216        assert!(too_long.parse::<Nickname>().is_err());
3217        assert!(not_ascii.parse::<Nickname>().is_err());
3218        assert!(too_short.parse::<Nickname>().is_err());
3219        assert!(other_invalid.parse::<Nickname>().is_err());
3220
3221        Ok(())
3222    }
3223
3224    /// Test for both `Hostname` and `InternetHost`
3225    #[test]
3226    fn hostname() {
3227        use std::net::IpAddr;
3228
3229        // Test a string that we should treat as a valid hostname.
3230        let chk_name = |s: &str| {
3231            let n: Hostname = s.parse().expect(s);
3232            assert_eq!(n.as_str(), s);
3233            assert_eq!(n.to_string(), s);
3234            assert_eq!(s.parse::<InternetHost>().expect(s), InternetHost::Name(n));
3235        };
3236
3237        // Test a string that looks like it could be an address or a hostname.
3238        // We parse those as addresses.
3239        let chk_either = |s: &str| {
3240            let h: InternetHost = s.parse().expect(s);
3241            let a: IpAddr = s.parse().expect(s);
3242            assert_eq!(h, InternetHost::IpAddr(a), "{s:?}");
3243            assert_eq!(h.to_string(), a.to_string(), "{s:?}");
3244        };
3245
3246        // Test a string that's an address, and isn't a valid hostname.
3247        let chk_addr = |s: &str| {
3248            let _: InvalidHostname = s.parse::<Hostname>().expect_err(s);
3249            chk_either(s);
3250        };
3251
3252        // Test a string that we should reject.
3253        let chk_bad = |s: &str| {
3254            let _: InvalidHostname = s.parse::<Hostname>().expect_err(s);
3255            let _: InvalidInternetHost = s.parse::<InternetHost>().expect_err(s);
3256        };
3257
3258        chk_name("foo.bar");
3259        chk_name("localhost");
3260        chk_name("tor.invalid");
3261        chk_name("example.com");
3262
3263        // Unarguably invalid.
3264        chk_bad("");
3265        chk_bad("foo bar");
3266        chk_bad("foo..bar");
3267        chk_bad("foo.-bar");
3268        chk_bad(" foo.bar ");
3269        chk_bad("[::1]");
3270
3271        // Strings that some IP address parsers accept as addresses,
3272        // but which are also valid hostnames according to RFC1123.
3273        //
3274        // We reject them rather than processing of them as hostnames,
3275        // exposing downstream software to possible strangeness.
3276        chk_bad("1");
3277        chk_bad("127.0.0.023");
3278
3279        // No-one thinks this is a valid IP address but we reject it as a hostname too,
3280        // even though it's syntactically legal per RFC1123, because it's quite bad.
3281        chk_bad("1.2.3.4.5");
3282
3283        chk_either("0.0.0.0");
3284        chk_either("127.0.0.1");
3285        chk_addr("::");
3286        chk_addr("::1");
3287        chk_addr("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
3288        chk_addr("::ffff:192.0.2.3"); // IPv6-mapped IPv4 address
3289    }
3290
3291    #[test]
3292    fn contact_info() -> anyhow::Result<()> {
3293        use encode::NetdocEncodable;
3294        use parse2::{ParseInput, parse_netdoc};
3295
3296        const S: &str = "some relay operator";
3297        let n: ContactInfo = S.parse()?;
3298        assert_eq!(n.as_str(), S);
3299        assert_eq!(n.to_string(), S);
3300
3301        let bad = |s: &str| {
3302            let _: InvalidContactInfo = s.parse::<ContactInfo>().unwrap_err();
3303        };
3304
3305        bad(" starts with space");
3306        bad("contains\nnewline");
3307
3308        #[derive(PartialEq, Debug, Deftly)]
3309        #[derive_deftly(NetdocParseable, NetdocEncodable)]
3310        struct TestDoc {
3311            pub intro: (),
3312            pub contact: ContactInfo,
3313        }
3314
3315        let roundtrip = |s: &str| -> anyhow::Result<()> {
3316            let doc = TestDoc {
3317                intro: (),
3318                contact: s.parse()?,
3319            };
3320            let mut enc = NetdocEncoder::new();
3321            doc.encode_unsigned(&mut enc)?;
3322            let enc = enc.finish()?;
3323            let reparsed = parse_netdoc::<TestDoc>(&ParseInput::new(&enc, "<test>"))?;
3324            assert_eq!(doc, reparsed);
3325            Ok(())
3326        };
3327
3328        roundtrip("normal")?;
3329        roundtrip("trailing  white space  ")?;
3330        roundtrip("wtf is this allowed in \x03 netdocs\r")?; // TODO torspec#396
3331
3332        Ok(())
3333    }
3334
3335    /// Round trip test for [`NumericBoolean`] ensuring that `0` is false,
3336    /// `1` is true, and other things fail.
3337    #[test]
3338    fn numeric_boolean() {
3339        let chk = |s: &str| {
3340            assert_eq!(NumericBoolean::from_str(s).expect(s).to_string(), s);
3341        };
3342        chk("0");
3343        chk("1");
3344        // Testing this because it is not a u8.
3345        assert!(NumericBoolean::from_str("10000").is_err());
3346    }
3347
3348    #[test]
3349    fn f64_finite() {
3350        let normalise_string = |i: &str, o: &str| {
3351            let v: F64Finite = i.parse().expect(i);
3352            assert_eq!(v.to_string(), o, "i={i:?}");
3353        };
3354        let roundtrip_string = |s: &str| normalise_string(s, s);
3355        let roundtrip_value = |i: f64| {
3356            let v: F64Finite = i.try_into().unwrap();
3357            let s = v.to_string();
3358            let o: F64Finite = s.parse().expect(&s);
3359            assert_eq!(v, o, "{i:?} {s}");
3360            assert_eq!(v.to_bits(), o.to_bits(), "{i:?} {s}");
3361        };
3362        let error_string = |s: &str| {
3363            let _: F64FiniteParseError = s.parse::<F64Finite>().expect_err(s);
3364        };
3365
3366        roundtrip_string("0");
3367        roundtrip_string("0.5");
3368        roundtrip_string("1");
3369        roundtrip_string("42");
3370        roundtrip_string("9007199254740991"); // f64::MAX_EXACT_INTEGER (as per Rust 1.96.0)
3371        normalise_string("1e3", "1000");
3372
3373        roundtrip_value(f64::EPSILON);
3374        roundtrip_value(f64::EPSILON + 1.0);
3375        roundtrip_value(f64::MIN);
3376        roundtrip_value(f64::MIN_POSITIVE);
3377        roundtrip_value(-f64::MIN_POSITIVE);
3378        roundtrip_value(f64::MAX);
3379
3380        error_string(&f64::NAN.to_string());
3381        error_string(&f64::INFINITY.to_string());
3382        error_string("");
3383        error_string("garbage");
3384
3385        // TODO torspec#416 these ought to be more reasonable, but this is what it does now:
3386        roundtrip_string(
3387            "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014",
3388        ); // MIN_POSITIVE
3389        roundtrip_string(
3390            "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
3391        ); // MAX
3392    }
3393
3394    /// Test that ensures SpFingerprint matches the 10x4 requirement.
3395    #[test]
3396    fn sp_fingerprint() {
3397        use derive_deftly::Deftly;
3398        use tor_llcrypto::pk::rsa::RsaIdentity;
3399
3400        use crate::parse2::ErrorProblem;
3401
3402        #[derive(Deftly)]
3403        #[derive_deftly(NetdocParseable, NetdocEncodable)]
3404        struct Wrapper {
3405            #[deftly(netdoc(single_arg))]
3406            fingerprint: SpFingerprint,
3407        }
3408
3409        /// Small helper to parse an [`SpFingerprint`].
3410        ///
3411        /// In the case the parsing went successful, it also performs a
3412        /// round-trip encoding test.
3413        fn parse2(s: &str) -> std::result::Result<SpFingerprint, ErrorProblem> {
3414            use crate::parse2::{self, ParseInput};
3415
3416            let input = format!("fingerprint {s}\n");
3417            let res = parse2::parse_netdoc::<Wrapper>(&ParseInput::new(&input, ""))
3418                .map_err(|x| x.problem)?;
3419
3420            // Round-trip encoding; we only do .starts_with() because input
3421            // may contain trailing parameters which will obviously not be
3422            // encoded; trimming to remove the trailing "\n" afterwards.
3423            let mut enc = NetdocEncoder::default();
3424            res.encode_unsigned(&mut enc).unwrap();
3425            assert!(input.starts_with(enc.finish().unwrap().trim_end()));
3426
3427            Ok(res.fingerprint)
3428        }
3429
3430        // Test a valid one.
3431        assert_eq!(
3432            parse2(&vec!["ABAB"; 10].join(" ")).unwrap(),
3433            SpFingerprint(RsaIdentity::from_bytes(&[0xAB; 20]).unwrap())
3434        );
3435
3436        // Test a valid one with tail.
3437        assert_eq!(
3438            parse2(&vec!["ABAB"; 11].join(" ")).unwrap(),
3439            SpFingerprint(RsaIdentity::from_bytes(&[0xAB; 20]).unwrap())
3440        );
3441
3442        // Missing argument
3443        assert!(matches!(
3444            parse2(&vec!["ABAB"; 9].join(" ")).unwrap_err(),
3445            ErrorProblem::MissingArgument { .. }
3446        ));
3447
3448        // Invalid argument.
3449        // In this case, we have string with a total length of 40 but with
3450        // one pair having 6 characters and another one having 2 as a proof
3451        // of that.
3452        assert!(matches!(
3453            parse2("0000 000000 00 0000 0000 0000 0000 0000 0000 0000").unwrap_err(),
3454            ErrorProblem::InvalidArgument { .. }
3455        ));
3456
3457        // And of course invalid hex should fail too.
3458        assert!(matches!(
3459            parse2(&vec!["ZZZZ"; 10].join(" ")).unwrap_err(),
3460            ErrorProblem::InvalidArgument { .. }
3461        ));
3462    }
3463
3464    /// Verifies the parsing of [`ItemPresent`].
3465    #[test]
3466    fn item_present_parse2() {
3467        #[derive(Default)]
3468        struct Token;
3469
3470        #[derive(Deftly)]
3471        #[derive_deftly(NetdocParseable)]
3472        struct TestDoc {
3473            #[allow(unused)]
3474            intro: Ignored,
3475            foo: Option<ItemPresent<Token>>,
3476        }
3477
3478        // The test cases with their respective result; boolean indicating that
3479        // it was present.
3480        let tests = [
3481            // Test valid present.
3482            ("intro\nfoo\n", Ok(true)),
3483            // Test valid absent.
3484            ("intro\n", Ok(false)),
3485            // Test repeated.
3486            ("intro\nfoo\nfoo\n", Err(ErrorProblem::ItemRepeated)),
3487            // Test repeated with unknown.
3488            ("intro\nbar\nfoo\nfoo\n", Err(ErrorProblem::ItemRepeated)),
3489            // Test with argument.
3490            ("intro\nfoo bar\n", Ok(true)),
3491            // Test with two arguments.
3492            ("intro\nfoo bar baz\n", Ok(true)),
3493            // Test with object.
3494            (
3495                "intro\nfoo\n-----BEGIN RSA PUBLIC KEY-----\n-----END RSA PUBLIC KEY-----\n",
3496                Err(ErrorProblem::ObjectUnexpected),
3497            ),
3498        ];
3499
3500        for (input, expect) in tests {
3501            println!("{input:?}, {expect:?}");
3502
3503            // Convert the result by calling .is_present() and extracting EP.
3504            let got = parse2::parse_netdoc::<TestDoc>(&ParseInput::new(input, ""))
3505                .map(|x| x.foo.is_some())
3506                .map_err(|e| e.problem);
3507            assert_eq!(got, expect);
3508        }
3509    }
3510
3511    #[test]
3512    fn item_present_encode() {
3513        #[derive(Default)]
3514        struct Token;
3515
3516        #[derive(Deftly)]
3517        #[derive_deftly(NetdocEncodable)]
3518        struct TestDoc {
3519            #[allow(unused)]
3520            intro: (),
3521            foo: Option<ItemPresent<Token>>,
3522        }
3523
3524        let tests = [
3525            (Some(ItemPresent(Token)), "intro\nfoo\n"),
3526            (None, "intro\n"),
3527        ];
3528
3529        for (present, output) in tests {
3530            let mut encoder = NetdocEncoder::new();
3531            TestDoc {
3532                intro: (),
3533                foo: present,
3534            }
3535            .encode_unsigned(&mut encoder)
3536            .unwrap();
3537            assert_eq!(encoder.finish().unwrap(), output);
3538        }
3539    }
3540
3541    #[test]
3542    fn ntor_onion_key_cross_cert() {
3543        // Dummy helper for parsing a subset of a router desc.
3544        #[derive(Debug, Deftly)]
3545        #[derive_deftly(NetdocParseable)]
3546        #[allow(unused)]
3547        struct TestDoc {
3548            /// Intro item.
3549            router: RouterDescIntroItem,
3550
3551            /// Timestamp used for `now` in certificate validation.
3552            #[deftly(netdoc(single_arg))]
3553            published: Iso8601TimeSp,
3554
3555            /// Required to ensure certified key of the crosscert.
3556            #[deftly(netdoc(single_arg))]
3557            master_key_ed25519: Ed25519Public,
3558
3559            /// Required to obtain the key signing the crosscert.
3560            #[deftly(netdoc(single_arg))]
3561            ntor_onion_key: Curve25519Public,
3562
3563            /// The actual crosscert.
3564            ntor_onion_key_crosscert: NtorOnionKeyCrossCert,
3565        }
3566
3567        impl TestDoc {
3568            // Quick verify helper.
3569            fn verify(&self, now: SystemTime) {
3570                Ed25519NtorCrossCert::verify(
3571                    // Converts X25519 to Ed25519.
3572                    tor_llcrypto::pk::keymanip::convert_curve25519_to_ed25519_public(
3573                        &self.ntor_onion_key.0,
3574                        self.ntor_onion_key_crosscert.bit.0.into(),
3575                    )
3576                    .unwrap()
3577                    .into(),
3578                    self.master_key_ed25519.0,
3579                    self.ntor_onion_key_crosscert.cert.raw_unverified().clone(),
3580                )
3581                .unwrap()
3582                .is_valid_at(&now)
3583                .unwrap();
3584            }
3585        }
3586
3587        let descs = include_str!("../../testdata2/cached-descriptors.new");
3588        let descs = parse2::parse_netdoc_multiple::<TestDoc>(&ParseInput::new(
3589            descs,
3590            "cached-descriptors.new",
3591        ))
3592        .unwrap();
3593
3594        // Find the first with negative and first with positive X coordinate.
3595        let negative_rd = descs
3596            .iter()
3597            .find(|rd| rd.ntor_onion_key_crosscert.bit.0)
3598            .unwrap();
3599        let positive_rd = descs
3600            .iter()
3601            .find(|rd| !rd.ntor_onion_key_crosscert.bit.0)
3602            .unwrap();
3603
3604        negative_rd.verify(negative_rd.published.0);
3605        positive_rd.verify(positive_rd.published.0);
3606    }
3607
3608    /// Helper to call methods for edcerts.
3609    trait Ed25519CertTest: Sized + PartialEq + Eq + Debug {
3610        /// Creates a new instance.
3611        ///
3612        /// Used to create a struct in ad-hoc fashion for Eq comparison.
3613        fn new(
3614            signing_key: ed25519::Ed25519Identity,
3615            certified_key: ed25519::Ed25519Identity,
3616        ) -> Self;
3617
3618        /// Returns the expected [`CertType`].
3619        fn cert_type() -> CertType;
3620
3621        /// Calls .new_signed().
3622        ///
3623        /// This method is used to create an [`EmbeddedCert`] with a given
3624        /// signing key and a key that shall be certified.
3625        fn new_signed(
3626            signing_key: &ed25519::Keypair,
3627            certified_key: ed25519::Ed25519Identity,
3628            expiry: SystemTime,
3629        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug>;
3630
3631        /// Calls .verify().
3632        ///
3633        /// The method verifies a certificate given a pre-known certified key,
3634        /// the actual certificate, and a timestamp.
3635        fn verify(
3636            signing_key: Option<ed25519::Ed25519Identity>,
3637            certified_key: ed25519::Ed25519Identity,
3638            cert: KeyUnknownCert,
3639        ) -> StdResult<TimerangeBound<Self>, VerifyFailed>;
3640    }
3641
3642    impl Ed25519CertTest for Ed25519IdentityCert {
3643        fn new(
3644            signing_key: ed25519::Ed25519Identity,
3645            certified_key: ed25519::Ed25519Identity,
3646        ) -> Self {
3647            Self {
3648                id_ed25519: signing_key,
3649                sign_ed25519: certified_key,
3650            }
3651        }
3652
3653        fn cert_type() -> CertType {
3654            CertType::IDENTITY_V_SIGNING
3655        }
3656
3657        fn new_signed(
3658            signing_key: &ed25519::Keypair,
3659            certified_key: ed25519::Ed25519Identity,
3660            expiry: SystemTime,
3661        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
3662            Self::new_signed(signing_key, certified_key, expiry)
3663        }
3664
3665        fn verify(
3666            _signing_key: Option<ed25519::Ed25519Identity>,
3667            _certified_key: ed25519::Ed25519Identity,
3668            cert: KeyUnknownCert,
3669        ) -> StdResult<TimerangeBound<Self>, VerifyFailed> {
3670            Self::verify(cert)
3671        }
3672    }
3673
3674    impl Ed25519CertTest for Ed25519FamilyCert {
3675        fn new(
3676            signing_key: ed25519::Ed25519Identity,
3677            _certified_key: ed25519::Ed25519Identity,
3678        ) -> Self {
3679            Self {
3680                family_ed25519: signing_key,
3681            }
3682        }
3683
3684        fn cert_type() -> CertType {
3685            CertType::FAMILY_V_IDENTITY
3686        }
3687
3688        fn new_signed(
3689            signing_key: &ed25519::Keypair,
3690            certified_key: ed25519::Ed25519Identity,
3691            expiry: SystemTime,
3692        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
3693            Self::new_signed(signing_key, certified_key, expiry)
3694        }
3695
3696        fn verify(
3697            _signing_key: Option<ed25519::Ed25519Identity>,
3698            certified_key: ed25519::Ed25519Identity,
3699            cert: KeyUnknownCert,
3700        ) -> StdResult<TimerangeBound<Self>, VerifyFailed> {
3701            Self::verify(certified_key, cert)
3702        }
3703    }
3704
3705    impl Ed25519CertTest for Ed25519NtorCrossCert {
3706        fn new(
3707            _signing_key: ed25519::Ed25519Identity,
3708            _certified_key: ed25519::Ed25519Identity,
3709        ) -> Self {
3710            Self::dangerous_new_unverified()
3711        }
3712
3713        fn cert_type() -> CertType {
3714            CertType::NTOR_CC_IDENTITY
3715        }
3716
3717        fn new_signed(
3718            signing_key: &ed25519::Keypair,
3719            certified_key: ed25519::Ed25519Identity,
3720            expiry: SystemTime,
3721        ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
3722            Self::new_signed(&ExpandedKeypair::from(signing_key), certified_key, expiry)
3723        }
3724
3725        fn verify(
3726            signing_key: Option<ed25519::Ed25519Identity>,
3727            certified_key: ed25519::Ed25519Identity,
3728            cert: KeyUnknownCert,
3729        ) -> StdResult<TimerangeBound<Self>, VerifyFailed> {
3730            Self::verify(signing_key.unwrap(), certified_key, cert)
3731        }
3732    }
3733
3734    /// Converts from [`Iso8601TimeSp`] to [`SystemTime`]
3735    fn str_to_st(s: &str) -> SystemTime {
3736        Iso8601TimeSp::from_str(s).unwrap().0
3737    }
3738
3739    /// Tests a valid ad-hoc generated certificate.
3740    fn ed25519_cert_rng<T: Ed25519CertTest>() {
3741        let mut rng = testing_rng();
3742        let signing_key = ed25519::Keypair::generate(&mut rng);
3743        let certified_key = ed25519::Keypair::generate(&mut rng);
3744        let now = str_to_st("2000-01-01 06:00:00");
3745        let expiry = str_to_st("2000-01-01 12:00:00");
3746
3747        // Test ad-hoc generation.
3748        let embedded_cert =
3749            T::new_signed(&signing_key, certified_key.public_key().into(), expiry).unwrap();
3750        assert_eq!(
3751            *embedded_cert.get().unwrap(),
3752            T::new(
3753                signing_key.public_key().into(),
3754                certified_key.public_key().into()
3755            )
3756        );
3757
3758        // Verify ad-hoc certificate generation.
3759        let unverified = embedded_cert.raw_unverified().clone();
3760        assert_eq!(T::cert_type(), unverified.peek_cert_type());
3761        match unverified.peek_subject_key() {
3762            CertifiedKey::Ed25519(x) => assert_eq!(
3763                *x,
3764                ed25519::Ed25519Identity::from(certified_key.public_key())
3765            ),
3766            _ => panic!(),
3767        }
3768
3769        // Finally, see if .verify() agrees.
3770        T::verify(
3771            Some(signing_key.public_key().into()),
3772            certified_key.public_key().into(),
3773            unverified.clone(),
3774        )
3775        .unwrap()
3776        .is_valid_at(&now)
3777        .unwrap();
3778
3779        // See if .verify() also agrees when expired but with toleration.
3780        T::verify(
3781            Some(signing_key.public_key().into()),
3782            certified_key.public_key().into(),
3783            unverified,
3784        )
3785        .unwrap()
3786        .extend_tolerance(Duration::from_secs(60 * 60))
3787        .is_valid_at(&now)
3788        .unwrap();
3789    }
3790
3791    /// Tests invalid Ed25519 certificates by violating various constraints.
3792    fn ed25519_cert_invalid<T: Ed25519CertTest + 'static>(requires_signed_with_ext: bool) {
3793        let mut rng = testing_rng();
3794        let now = str_to_st("2000-01-01 06:00:00");
3795        let expiry = str_to_st("2000-01-01 12:00:00");
3796        let signing_key = ed25519::Keypair::generate(&mut rng);
3797        let signing_pk = ed25519::Ed25519Identity::from(signing_key.public_key());
3798        let certified_key = ed25519::Keypair::generate(&mut rng);
3799        let certified_pk = ed25519::Ed25519Identity::from(certified_key.public_key());
3800
3801        let mut tests: Vec<(_, _, CertifiedKey, _, _)> = vec![
3802            // Testing a violation of the signature is hard because the encoder
3803            // refuses to emit such a thing.
3804            // ---
3805            // Violate timestamp.
3806            (
3807                T::cert_type(),
3808                // We achieve this by setting expiry to now - 1 day.
3809                now - Duration::from_secs(64 * 64 * 24),
3810                certified_pk.into(),
3811                Some(&signing_pk),
3812                &signing_key,
3813            ),
3814            // Violate cert type.
3815            (
3816                // Just picking something completely out of place here.
3817                CertType::LINK_AUTH_X509,
3818                expiry,
3819                certified_pk.into(),
3820                Some(&signing_pk),
3821                &signing_key,
3822            ),
3823            // Violate certified key type.
3824            (
3825                T::cert_type(),
3826                expiry,
3827                // Just pass a different CertifiedKey variant here.
3828                CertifiedKey::RsaSha256Digest(certified_pk.into()),
3829                Some(&signing_pk),
3830                &signing_key,
3831            ),
3832            // ---
3833            // Missing test for violating both keys MUST be valid mappings to a
3834            // [`ed25519::PublicKey`].  I was unable to find a single test
3835            // vector for this, even in curve25591-dalek. :/
3836        ];
3837
3838        // Violate absence of `signed-with-ed25519-key`.
3839        // This is not a violation in Ed25519NtorCrossCert.
3840        if requires_signed_with_ext {
3841            tests.push((
3842                T::cert_type(),
3843                expiry,
3844                certified_pk.into(),
3845                None,
3846                &signing_key,
3847            ));
3848        }
3849
3850        for (ctype, expiry, certified_key, signing_key, signing_kp) in tests {
3851            let mut builder = Ed25519Cert::builder()
3852                .cert_type(ctype)
3853                .expiration(expiry)
3854                .cert_key(certified_key.clone())
3855                .clone();
3856            if let Some(signing_key) = signing_key {
3857                builder = builder.signing_key(*signing_key).clone();
3858            }
3859            let cert = Ed25519Cert::decode(&builder.encode_and_sign(signing_kp).unwrap()).unwrap();
3860
3861            // We purposely always create an Ed25519Identity here from the bytes
3862            // in order to make it possible to test for invalid certified
3863            // key types.
3864            T::verify(
3865                signing_key.copied(),
3866                Ed25519Identity::from_bytes(certified_key.as_bytes()).unwrap(),
3867                cert,
3868            )
3869            .and_then(|expired| expired.is_valid_at(&now).map_err(|e| e.into()))
3870            .unwrap_err();
3871        }
3872    }
3873
3874    #[test]
3875    fn ed25519_cert_rng_test() {
3876        ed25519_cert_rng::<Ed25519IdentityCert>();
3877        ed25519_cert_rng::<Ed25519FamilyCert>();
3878        ed25519_cert_rng::<Ed25519NtorCrossCert>();
3879    }
3880
3881    #[test]
3882    fn ed25519_cert_invalid_test() {
3883        ed25519_cert_invalid::<Ed25519IdentityCert>(true);
3884        ed25519_cert_invalid::<Ed25519FamilyCert>(true);
3885        ed25519_cert_invalid::<Ed25519NtorCrossCert>(false);
3886    }
3887}