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

            
8
pub(crate) use b16impl::*;
9
pub use b64impl::*;
10
pub(crate) use curve25519impl::*;
11
pub(crate) use ed25519impl::*;
12
#[cfg(any(feature = "routerdesc", feature = "hs-common"))]
13
pub(crate) use edcert::*;
14
pub(crate) use fingerprint::*;
15
pub(crate) use rsa::*;
16
pub use timeimpl::*;
17

            
18
#[cfg(feature = "encode")]
19
use {
20
    crate::encode::{
21
        self,
22
        ItemEncoder,
23
        ItemObjectEncodable,
24
        ItemValueEncodable,
25
        // `E` for "encode`; different from `parse2::MultiplicitySelector`
26
        MultiplicitySelector as EMultiplicitySelector,
27
    },
28
    std::iter,
29
};
30
#[cfg(feature = "parse2")]
31
use {
32
    crate::parse2::multiplicity::{
33
        ItemSetMethods,
34
        // `P2` for "parse2`; different from `encode::MultiplicitySelector`
35
        MultiplicitySelector as P2MultiplicitySelector,
36
        ObjectSetMethods,
37
    },
38
    crate::parse2::{ArgumentError, ArgumentStream, ItemArgumentParseable, ItemObjectParseable}, //
39
};
40

            
41
pub use nickname::Nickname;
42

            
43
pub use fingerprint::{Base64Fingerprint, Fingerprint};
44

            
45
pub use identified_digest::{DigestName, IdentifiedDigest};
46

            
47
pub use ignored_impl::{Ignored, IgnoredItemOrObjectValue, NotPresent};
48

            
49
use crate::NormalItemArgument;
50
use derive_deftly::{Deftly, define_derive_deftly};
51
use std::cmp::{self, PartialOrd};
52
use std::fmt::{self, Display};
53
use std::marker::PhantomData;
54
use std::str::FromStr;
55
use tor_error::{Bug, ErrorReport as _, internal};
56
use void::{ResultVoidExt as _, Void};
57

            
58
/// Describes a value that van be decoded from a bunch of bytes.
59
///
60
/// Used for decoding the objects between BEGIN and END tags.
61
pub(crate) trait FromBytes: Sized {
62
    /// Try to parse a value of this type from a byte slice
63
    fn from_bytes(b: &[u8], p: crate::Pos) -> crate::Result<Self>;
64
    /// Try to parse a value of this type from a vector of bytes,
65
    /// and consume that value
66
4908
    fn from_vec(v: Vec<u8>, p: crate::Pos) -> crate::Result<Self> {
67
4908
        Self::from_bytes(&v[..], p)
68
4908
    }
69
}
70

            
71
/// Types for decoding base64-encoded values.
72
mod b64impl {
73
    use crate::{Error, NetdocErrorKind as EK, Pos, Result};
74
    use base64ct::{Base64, Base64Unpadded, Encoding};
75
    use std::fmt::{self, Display};
76
    use std::ops::RangeBounds;
77
    use subtle::{Choice, ConstantTimeEq};
78

            
79
    /// A byte array, encoded in base64 with optional padding.
80
    ///
81
    /// On output (`Display`), output is unpadded.
82
    #[derive(Clone)]
83
    #[allow(clippy::derived_hash_with_manual_eq)]
84
    #[derive(Hash, derive_more::Debug, derive_more::From, derive_more::Into)]
85
    #[debug(r#"B64("{self}")"#)]
86
    pub struct B64(Vec<u8>);
87

            
88
    impl ConstantTimeEq for B64 {
89
        fn ct_eq(&self, other: &B64) -> Choice {
90
            self.0.ct_eq(&other.0)
91
        }
92
    }
93
    /// `B64` is `Eq` via its constant-time implementation.
94
    impl PartialEq for B64 {
95
        fn eq(&self, other: &B64) -> bool {
96
            self.ct_eq(other).into()
97
        }
98
    }
99
    impl Eq for B64 {}
100

            
101
    impl std::str::FromStr for B64 {
102
        type Err = Error;
103
71453
        fn from_str(s: &str) -> Result<Self> {
104
71453
            let v: core::result::Result<Vec<u8>, base64ct::Error> = match s.len() % 4 {
105
10758
                0 => Base64::decode_vec(s),
106
60695
                _ => Base64Unpadded::decode_vec(s),
107
            };
108
89460
            let v = v.map_err(|_| {
109
36014
                EK::BadArgument
110
36014
                    .with_msg("Invalid base64")
111
36014
                    .at_pos(Pos::at(s))
112
54021
            })?;
113
35439
            Ok(B64(v))
114
71453
        }
115
    }
116

            
117
    impl Display for B64 {
118
28
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119
28
            Display::fmt(&Base64Unpadded::encode_string(&self.0), f)
120
28
        }
121
    }
122

            
123
    impl B64 {
124
        /// Return the byte array from this object.
125
19053
        pub fn as_bytes(&self) -> &[u8] {
126
19053
            &self.0[..]
127
19053
        }
128
        /// Return this object if its length is within the provided bounds
129
        /// object, or an error otherwise.
130
1932
        pub(crate) fn check_len<B: RangeBounds<usize>>(self, bounds: B) -> Result<Self> {
131
1932
            if bounds.contains(&self.0.len()) {
132
1930
                Ok(self)
133
            } else {
134
2
                Err(EK::BadObjectVal.with_msg("Invalid length on base64 data"))
135
            }
136
1932
        }
137

            
138
        /// Try to convert this object into an array of N bytes.
139
        ///
140
        /// Return an error if the length is wrong.
141
22110
        pub fn into_array<const N: usize>(self) -> Result<[u8; N]> {
142
22110
            self.0
143
22110
                .try_into()
144
22110
                .map_err(|_| EK::BadObjectVal.with_msg("Invalid length on base64 data"))
145
22110
        }
146
    }
147
}
148

            
149
// ============================================================
150

            
151
/// Types for decoding hex-encoded values.
152
mod b16impl {
153
    use crate::{Error, NetdocErrorKind as EK, Pos, Result};
154

            
155
    /// A byte array encoded in hexadecimal.
156
    pub(crate) struct B16(Vec<u8>);
157

            
158
    impl std::str::FromStr for B16 {
159
        type Err = Error;
160
1083
        fn from_str(s: &str) -> Result<Self> {
161
1085
            let bytes = hex::decode(s).map_err(|_| {
162
4
                EK::BadArgument
163
4
                    .at_pos(Pos::at(s))
164
4
                    .with_msg("invalid hexadecimal")
165
6
            })?;
166
1079
            Ok(B16(bytes))
167
1083
        }
168
    }
169

            
170
    impl B16 {
171
        /// Return the underlying byte array.
172
        #[allow(unused)]
173
6
        pub(crate) fn as_bytes(&self) -> &[u8] {
174
6
            &self.0[..]
175
6
        }
176
    }
177

            
178
    impl From<B16> for Vec<u8> {
179
1073
        fn from(w: B16) -> Vec<u8> {
180
1073
            w.0
181
1073
        }
182
    }
183
}
184

            
185
// ============================================================
186

            
187
/// Types for decoding curve25519 keys
188
mod curve25519impl {
189
    use super::B64;
190
    use crate::{Error, NetdocErrorKind as EK, Pos, Result};
191
    use tor_llcrypto::pk::curve25519::PublicKey;
192

            
193
    /// A Curve25519 public key, encoded in base64 with optional padding
194
    pub(crate) struct Curve25519Public(PublicKey);
195

            
196
    impl std::str::FromStr for Curve25519Public {
197
        type Err = Error;
198
2504
        fn from_str(s: &str) -> Result<Self> {
199
2504
            let b64: B64 = s.parse()?;
200
2497
            let array: [u8; 32] = b64.as_bytes().try_into().map_err(|_| {
201
2
                EK::BadArgument
202
2
                    .at_pos(Pos::at(s))
203
2
                    .with_msg("bad length for curve25519 key.")
204
3
            })?;
205
2494
            Ok(Curve25519Public(array.into()))
206
2504
        }
207
    }
208

            
209
    impl From<Curve25519Public> for PublicKey {
210
2494
        fn from(w: Curve25519Public) -> PublicKey {
211
2494
            w.0
212
2494
        }
213
    }
214
}
215

            
216
// ============================================================
217

            
218
/// Types for decoding ed25519 keys
219
mod ed25519impl {
220
    use super::B64;
221
    use crate::{Error, NetdocErrorKind as EK, Pos, Result};
222
    use tor_llcrypto::pk::ed25519::Ed25519Identity;
223

            
224
    /// An alleged ed25519 public key, encoded in base64 with optional
225
    /// padding.
226
    pub(crate) struct Ed25519Public(Ed25519Identity);
227

            
228
    impl std::str::FromStr for Ed25519Public {
229
        type Err = Error;
230
2502
        fn from_str(s: &str) -> Result<Self> {
231
2502
            let b64: B64 = s.parse()?;
232
2496
            if b64.as_bytes().len() != 32 {
233
                return Err(EK::BadArgument
234
                    .at_pos(Pos::at(s))
235
                    .with_msg("bad length for ed25519 key."));
236
2496
            }
237
2496
            let key = Ed25519Identity::from_bytes(b64.as_bytes()).ok_or_else(|| {
238
                EK::BadArgument
239
                    .at_pos(Pos::at(s))
240
                    .with_msg("bad value for ed25519 key.")
241
            })?;
242
2496
            Ok(Ed25519Public(key))
243
2502
        }
244
    }
245

            
246
    impl From<Ed25519Public> for Ed25519Identity {
247
2496
        fn from(pk: Ed25519Public) -> Ed25519Identity {
248
2496
            pk.0
249
2496
        }
250
    }
251
}
252

            
253
// ============================================================
254

            
255
/// Dummy types like [`Ignored`]
256
mod ignored_impl {
257
    use super::*;
258

            
259
    #[cfg(feature = "parse2")]
260
    use crate::parse2::ErrorProblem as EP;
261

            
262
    /// Part of a network document, that isn't actually there.
263
    ///
264
    /// Used as a standin in `ns_type!` calls in various netstatus `each_variety.rs`.
265
    /// The effect is as if the field were omitted from the containing type.
266
    ///
267
    ///  * When used as item(s) (ie, a field type when deriving `NetdocParseable\[Fields\]`):
268
    ///    **ignores any number** of items with that field's keyword during parsing,
269
    ///    and emits none during encoding.
270
    ///
271
    ///    (To *reject* documents containing this item, use `Option<Void>`,
272
    ///    but note that the spec says unknown items should be ignored,
273
    ///    which would normally include items which are merely missing from one variety.)
274
    ///
275
    ///  * When used as an argument (ie, a field type when deriving `ItemValueParseable`,
276
    ///    or with `netdoc(single_arg)`  when deriving `NetdocParseable\[Fields\]`):
277
    ///    consumes **no arguments** during parsing, and emits none during encoding.
278
    ///
279
    ///  * When used as an object field (ie, `netdoc(object)` when deriving `ItemValueParseable`):
280
    ///    **rejects** an object - failing the parse if one is present.
281
    ///    (Functions similarly to `Option<Void>`, but prefer `NotPresent` as it's clearer.)
282
    ///
283
    /// There are bespoke impls of the multiplicity traits
284
    /// `ItemSetMethods` and `ObjectSetMethods`:
285
    /// don't wrap this type in `Option` or `Vec`.
286
    //
287
    // TODO we'll need to implement ItemArgument etc., for encoding, too.
288
    #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Default)]
289
    #[allow(clippy::exhaustive_structs)]
290
    #[cfg_attr(
291
        feature = "parse2",
292
        derive(Deftly),
293
        derive_deftly(NetdocParseableFields)
294
    )]
295
    pub struct NotPresent;
296

            
297
    /// Ignored part of a network document.
298
    ///
299
    /// With `parse2`, can be used as an item, object, or even flattened-fields.
300
    ///
301
    /// When deriving `parse2` traits, and a field is absent in a particular netstatus variety,
302
    /// use `ns_type!` with [`NotPresent`], rather than `Ignored`.
303
    ///
304
    /// During encoding as an Items or Objects, will be entirely omitted,
305
    /// via the multiplicity arrangements.
306
    ///
307
    /// Cannot be encoded as an Argument: if this is not the last
308
    /// Argument, we need something to put into the output document to avoid generating
309
    /// a document with the arguments out of step.  If it *is* the last argument,
310
    /// it could simply be omitted, since additional arguments are in any case ignored.
311
    #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Default)]
312
    #[cfg_attr(
313
        feature = "parse2",
314
        derive(Deftly),
315
        derive_deftly(ItemValueParseable, NetdocParseableFields)
316
    )]
317
    #[allow(clippy::exhaustive_structs)]
318
    pub struct Ignored;
319

            
320
    /// An Item or Object that would be ignored during parsing and is omitted during encoding
321
    ///
322
    /// This is the "single" item type for encoding multiplicity for Items or Objects,
323
    /// for [`Ignored`].
324
    ///
325
    /// This type is uninhabited.
326
    pub struct IgnoredItemOrObjectValue(Void);
327

            
328
    #[cfg(feature = "parse2")]
329
    impl ItemSetMethods for P2MultiplicitySelector<NotPresent> {
330
        type Each = Ignored;
331
        type Field = NotPresent;
332
        fn can_accumulate(self, _acc: &Option<NotPresent>) -> Result<(), EP> {
333
            Ok(())
334
        }
335
12
        fn accumulate(self, _acc: &mut Option<NotPresent>, _item: Ignored) -> Result<(), EP> {
336
12
            Ok(())
337
12
        }
338
32
        fn finish(self, _acc: Option<NotPresent>, _: &'static str) -> Result<NotPresent, EP> {
339
32
            Ok(NotPresent)
340
32
        }
341
    }
342

            
343
    #[cfg(feature = "parse2")]
344
    impl ItemArgumentParseable for NotPresent {
345
12
        fn from_args(_: &mut ArgumentStream) -> Result<NotPresent, ArgumentError> {
346
12
            Ok(NotPresent)
347
12
        }
348
    }
349

            
350
    #[cfg(feature = "parse2")]
351
    impl ObjectSetMethods for P2MultiplicitySelector<NotPresent> {
352
        type Field = NotPresent;
353
        type Each = Void;
354
6
        fn resolve_option(self, _found: Option<Void>) -> Result<NotPresent, EP> {
355
6
            Ok(NotPresent)
356
6
        }
357
    }
358

            
359
    #[cfg(feature = "encode")]
360
    impl<'f> encode::MultiplicityMethods<'f> for EMultiplicitySelector<NotPresent> {
361
        type Field = NotPresent;
362
        type Each = Void;
363
6
        fn iter_ordered(self, _: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
364
6
            iter::empty()
365
6
        }
366
    }
367

            
368
    #[cfg(feature = "encode")]
369
    impl encode::OptionalityMethods for EMultiplicitySelector<NotPresent> {
370
        type Field = NotPresent;
371
        type Each = Void;
372
2
        fn as_option<'f>(self, _: &'f Self::Field) -> Option<&'f Self::Each> {
373
2
            None
374
2
        }
375
    }
376

            
377
    impl FromStr for Ignored {
378
        type Err = Void;
379
        fn from_str(_s: &str) -> Result<Ignored, Void> {
380
            Ok(Ignored)
381
        }
382
    }
383

            
384
    #[cfg(feature = "parse2")]
385
    impl ItemArgumentParseable for Ignored {
386
        fn from_args(_: &mut ArgumentStream) -> Result<Ignored, ArgumentError> {
387
            Ok(Ignored)
388
        }
389
    }
390

            
391
    #[cfg(feature = "parse2")]
392
    impl ItemObjectParseable for Ignored {
393
6
        fn check_label(_label: &str) -> Result<(), EP> {
394
            // allow any label
395
6
            Ok(())
396
6
        }
397
6
        fn from_bytes(_input: &[u8]) -> Result<Self, EP> {
398
6
            Ok(Ignored)
399
6
        }
400
    }
401

            
402
    #[cfg(feature = "parse2")]
403
    impl ObjectSetMethods for P2MultiplicitySelector<Ignored> {
404
        type Field = Ignored;
405
        type Each = Ignored;
406
6
        fn resolve_option(self, _found: Option<Ignored>) -> Result<Ignored, EP> {
407
6
            Ok(Ignored)
408
6
        }
409
    }
410

            
411
    #[cfg(feature = "encode")]
412
    impl<'f> encode::MultiplicityMethods<'f> for EMultiplicitySelector<Ignored> {
413
        type Field = Ignored;
414
        type Each = IgnoredItemOrObjectValue;
415
        fn iter_ordered(self, _: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
416
            iter::empty()
417
        }
418
    }
419

            
420
    #[cfg(feature = "encode")]
421
    impl encode::OptionalityMethods for EMultiplicitySelector<Ignored> {
422
        type Field = Ignored;
423
        type Each = IgnoredItemOrObjectValue;
424
2
        fn as_option<'f>(self, _: &'f Self::Field) -> Option<&'f Self::Each> {
425
2
            None
426
2
        }
427
    }
428

            
429
    #[cfg(feature = "encode")]
430
    impl ItemValueEncodable for IgnoredItemOrObjectValue {
431
        fn write_item_value_onto(&self, _: ItemEncoder) -> Result<(), Bug> {
432
            void::unreachable(self.0)
433
        }
434
    }
435

            
436
    #[cfg(feature = "encode")]
437
    impl ItemObjectEncodable for IgnoredItemOrObjectValue {
438
        fn label(&self) -> &str {
439
            void::unreachable(self.0)
440
        }
441
        fn write_object_onto(&self, _: &mut Vec<u8>) -> Result<(), Bug> {
442
            void::unreachable(self.0)
443
        }
444
    }
445
}
446

            
447
// ============================================================
448

            
449
/// Information about unknown values, which may have been retained as a `T`
450
///
451
/// Won't grow additional variants - but, `Retained` is only included conditionally.
452
///
453
/// Also used in the form `Unknown<()>` to indicate whether unknown values *should* be retained.
454
///
455
/// ### Example
456
///
457
/// ```
458
/// # {
459
/// #![cfg(feature = "retain-unknown")]
460
///
461
/// use tor_netdoc::types::Unknown;
462
///
463
/// let mut unk: Unknown<Vec<String>> = Unknown::new_retained_default();
464
/// unk.with_mut_unknown(|u| u.push("something-we-found".into()));
465
/// assert_eq!(unk.into_retained().unwrap(), ["something-we-found"]);
466
/// # }
467
/// ```
468
///
469
/// ### Equality comparison, semantics
470
///
471
/// Two `Unknown` are consider equal if both have the same record of unknown values,
472
/// or if neither records unknown values at all.
473
///
474
/// `Unknown` is not `Eq` or `Ord` because we won't want to relate a `Discarded`
475
/// to a `Retained`.  That would be a a logic error.  `partial_cmp` gives `None` for this.
476
#[derive(Debug, PartialEq, Clone, Copy, Hash)]
477
#[non_exhaustive]
478
pub enum Unknown<T> {
479
    /// The parsing discarded unknown values and they are no longer available.
480
    Discarded(PhantomData<T>),
481

            
482
    /// The document parsing retained (or should retain) unknown values.
483
    #[cfg(feature = "retain-unknown")]
484
    Retained(T),
485
}
486

            
487
impl<T> Unknown<T> {
488
    /// Create an `Unknown` which specifies that values were discarded (or should be)
489
420540
    pub fn new_discard() -> Self {
490
420540
        Unknown::Discarded(PhantomData)
491
420540
    }
492

            
493
    /// Map the `Retained`, if there is one
494
1960
    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Unknown<U> {
495
1960
        self.try_map(move |t| Ok::<_, Void>(f(t))).void_unwrap()
496
1960
    }
497

            
498
    /// Map the `Retained`, fallibly
499
1960
    pub fn try_map<U, E>(self, f: impl FnOnce(T) -> Result<U, E>) -> Result<Unknown<U>, E> {
500
1960
        Ok(match self {
501
1960
            Unknown::Discarded(_) => Unknown::Discarded(PhantomData),
502
            #[cfg(feature = "retain-unknown")]
503
            Unknown::Retained(t) => Unknown::Retained(f(t)?),
504
        })
505
1960
    }
506

            
507
    /// Obtain an `Unknown` containing (maybe) a reference
508
    pub fn as_ref(&self) -> Option<&T> {
509
        match self {
510
            Unknown::Discarded(_) => None,
511
            #[cfg(feature = "retain-unknown")]
512
            Unknown::Retained(t) => Some(t),
513
        }
514
    }
515

            
516
    /// Obtain the `Retained` data
517
    ///
518
    /// Treats lack of retention as an internal error.
519
    #[cfg(feature = "retain-unknown")]
520
    pub fn into_retained(self) -> Result<T, Bug> {
521
        match self {
522
            Unknown::Discarded(_) => Err(internal!("Unknown::retained but data not collected")),
523
            Unknown::Retained(t) => Ok(t),
524
        }
525
    }
526

            
527
    /// Start recording unknown information, with a default value for `T`
528
    #[cfg(feature = "retain-unknown")]
529
    pub fn new_retained_default() -> Self
530
    where
531
        T: Default,
532
    {
533
        Unknown::Retained(T::default())
534
    }
535

            
536
    /// Update the `Retained`, if there is one
537
    ///
538
    /// Intended for use in parsing, when we encounter an unknown value.
539
    ///
540
    /// Not provided in `try_` form.  If you think you need this, instead, unconditionally
541
    /// parse and verify the unknown value, and then conditionally insert it with this function.
542
    /// Don't parse it conditionally - that would skip some validation.
543
2
    pub fn with_mut_unknown(&mut self, f: impl FnOnce(&mut T)) {
544
2
        match self {
545
2
            Unknown::Discarded(_) => {}
546
            #[cfg(feature = "retain-unknown")]
547
            Unknown::Retained(t) => f(t),
548
        }
549
2
    }
550
}
551

            
552
impl<T: PartialOrd> PartialOrd for Unknown<T> {
553
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
554
        use Unknown::*;
555
        match (self, other) {
556
            (Discarded(_), Discarded(_)) => Some(cmp::Ordering::Equal),
557
            #[cfg(feature = "retain-unknown")]
558
            (Discarded(_), Retained(_)) | (Retained(_), Discarded(_)) => None,
559
            #[cfg(feature = "retain-unknown")]
560
            (Retained(a), Retained(b)) => a.partial_cmp(b),
561
        }
562
    }
563
}
564

            
565
// ============================================================
566

            
567
/// Types for decoding times and dates
568
mod timeimpl {
569
    use crate::{Error, NetdocErrorKind as EK, Pos, Result};
570
    use std::time::SystemTime;
571
    use time::{
572
        OffsetDateTime, PrimitiveDateTime, format_description::FormatItem,
573
        macros::format_description,
574
    };
575

            
576
    /// A wall-clock time, encoded in Iso8601 format with an intervening
577
    /// space between the date and time.
578
    ///
579
    /// (Example: "2020-10-09 17:38:12")
580
    #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] //
581
    #[derive(derive_more::Into, derive_more::From, derive_more::Deref)]
582
    #[allow(clippy::exhaustive_structs)]
583
    pub struct Iso8601TimeSp(pub SystemTime);
584

            
585
    /// Formatting object for parsing the space-separated Iso8601 format.
586
    const ISO_8601SP_FMT: &[FormatItem] =
587
        format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
588

            
589
    impl std::str::FromStr for Iso8601TimeSp {
590
        type Err = Error;
591
5709
        fn from_str(s: &str) -> Result<Iso8601TimeSp> {
592
5713
            let d = PrimitiveDateTime::parse(s, &ISO_8601SP_FMT).map_err(|e| {
593
8
                EK::BadArgument
594
8
                    .at_pos(Pos::at(s))
595
8
                    .with_msg(format!("invalid time: {}", e))
596
12
            })?;
597
5701
            Ok(Iso8601TimeSp(d.assume_utc().into()))
598
5709
        }
599
    }
600

            
601
    /// Formats a SystemTime according to the given format description
602
    ///
603
    /// Also converts any time::error::format to std::fmt::Error
604
    /// so that it can be unwrapped in the Display trait impl
605
14
    fn fmt_with(
606
14
        t: SystemTime,
607
14
        format_desc: &[FormatItem],
608
14
    ) -> core::result::Result<String, std::fmt::Error> {
609
14
        OffsetDateTime::from(t)
610
14
            .format(format_desc)
611
14
            .map_err(|_| std::fmt::Error)
612
14
    }
613

            
614
    impl std::fmt::Display for Iso8601TimeSp {
615
8
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
616
8
            write!(f, "{}", fmt_with(self.0, ISO_8601SP_FMT)?)
617
8
        }
618
    }
619

            
620
    /// A wall-clock time, encoded in ISO8601 format without an intervening
621
    /// space.
622
    ///
623
    /// This represents a specific UTC instant (ie an instant in global civil time).
624
    /// But it may not be able to represent leap seconds.
625
    ///
626
    /// The timezone is not included in the string representation; `+0000` is implicit.
627
    ///
628
    /// (Example: "2020-10-09T17:38:12")
629
    #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] //
630
    #[derive(derive_more::Into, derive_more::From, derive_more::Deref)]
631
    #[allow(clippy::exhaustive_structs)]
632
    pub struct Iso8601TimeNoSp(pub SystemTime);
633

            
634
    /// Formatting object for parsing the space-separated Iso8601 format.
635
    const ISO_8601NOSP_FMT: &[FormatItem] =
636
        format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]");
637

            
638
    impl std::str::FromStr for Iso8601TimeNoSp {
639
        type Err = Error;
640
24
        fn from_str(s: &str) -> Result<Iso8601TimeNoSp> {
641
28
            let d = PrimitiveDateTime::parse(s, &ISO_8601NOSP_FMT).map_err(|e| {
642
8
                EK::BadArgument
643
8
                    .at_pos(Pos::at(s))
644
8
                    .with_msg(format!("invalid time: {}", e))
645
12
            })?;
646
16
            Ok(Iso8601TimeNoSp(d.assume_utc().into()))
647
24
        }
648
    }
649

            
650
    impl std::fmt::Display for Iso8601TimeNoSp {
651
6
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
652
6
            write!(f, "{}", fmt_with(self.0, ISO_8601NOSP_FMT)?)
653
6
        }
654
    }
655

            
656
    impl crate::NormalItemArgument for Iso8601TimeNoSp {}
657
}
658

            
659
/// Types for decoding RSA keys
660
mod rsa {
661
    use crate::{NetdocErrorKind as EK, Pos, Result};
662
    use std::ops::RangeBounds;
663
    use tor_llcrypto::pk::rsa::PublicKey;
664

            
665
    /// The fixed exponent which we require when parsing any RSA key in a netdoc
666
    //
667
    // TODO this value is duplicated a lot in the v1 parser
668
    pub(crate) const RSA_FIXED_EXPONENT: u32 = 65537;
669

            
670
    /// The fixed exponent which we require when parsing any RSA key in a netdoc
671
    //
672
    // TODO this value is duplicated a lot in the v1 parser
673
    pub(crate) const RSA_MIN_BITS: usize = 1024;
674

            
675
    /// RSA public key, partially processed by `crate::paarse`.
676
    ///
677
    /// As parsed from a base64-encoded object.
678
    /// They key's properties (exponent and size) haven't been checked.
679
    #[allow(non_camel_case_types)]
680
    #[derive(Clone, Debug)]
681
    pub(crate) struct RsaPublicParse1Helper(PublicKey, Pos);
682

            
683
    impl From<RsaPublicParse1Helper> for PublicKey {
684
4906
        fn from(k: RsaPublicParse1Helper) -> PublicKey {
685
4906
            k.0
686
4906
        }
687
    }
688
    impl super::FromBytes for RsaPublicParse1Helper {
689
4908
        fn from_bytes(b: &[u8], pos: Pos) -> Result<Self> {
690
4908
            let key = PublicKey::from_der(b)
691
4909
                .ok_or_else(|| EK::BadObjectVal.with_msg("unable to decode RSA public key"))?;
692
4906
            Ok(RsaPublicParse1Helper(key, pos))
693
4908
        }
694
    }
695
    impl RsaPublicParse1Helper {
696
        /// Give an error if the exponent of this key is not 'e'
697
4908
        pub(crate) fn check_exponent(self, e: u32) -> Result<Self> {
698
4908
            if self.0.exponent_is(e) {
699
4906
                Ok(self)
700
            } else {
701
2
                Err(EK::BadObjectVal
702
2
                    .at_pos(self.1)
703
2
                    .with_msg("invalid RSA exponent"))
704
            }
705
4908
        }
706
        /// Give an error if the length of this key's modulus, in
707
        /// bits, is not contained in 'bounds'
708
4912
        pub(crate) fn check_len<B: RangeBounds<usize>>(self, bounds: B) -> Result<Self> {
709
4912
            if bounds.contains(&self.0.bits()) {
710
4908
                Ok(self)
711
            } else {
712
4
                Err(EK::BadObjectVal
713
4
                    .at_pos(self.1)
714
4
                    .with_msg("invalid RSA length"))
715
            }
716
4912
        }
717
        /// Give an error if the length of this key's modulus, in
718
        /// bits, is not exactly `n`.
719
4560
        pub(crate) fn check_len_eq(self, n: usize) -> Result<Self> {
720
4560
            self.check_len(n..=n)
721
4560
        }
722
    }
723
}
724

            
725
/// Types for decoding Ed25519 certificates
726
#[cfg(any(feature = "routerdesc", feature = "hs-common"))]
727
mod edcert {
728
    use crate::{NetdocErrorKind as EK, Pos, Result};
729
    use tor_cert::{CertType, Ed25519Cert, KeyUnknownCert};
730
    #[cfg(feature = "routerdesc")]
731
    use tor_llcrypto::pk::ed25519;
732

            
733
    /// An ed25519 certificate as parsed from a directory object, with
734
    /// signature not validated.
735
    #[derive(Debug, Clone)]
736
    pub(crate) struct UnvalidatedEdCert(KeyUnknownCert, Pos);
737

            
738
    impl super::FromBytes for UnvalidatedEdCert {
739
6839
        fn from_bytes(b: &[u8], p: Pos) -> Result<Self> {
740
6840
            let cert = Ed25519Cert::decode(b).map_err(|e| {
741
2
                EK::BadObjectVal
742
2
                    .at_pos(p)
743
2
                    .with_msg("Bad certificate")
744
2
                    .with_source(e)
745
3
            })?;
746

            
747
6837
            Ok(Self(cert, p))
748
6839
        }
749
6839
        fn from_vec(v: Vec<u8>, p: Pos) -> Result<Self> {
750
6839
            Self::from_bytes(&v[..], p)
751
6839
        }
752
    }
753
    impl UnvalidatedEdCert {
754
        /// Give an error if this certificate's type is not `desired_type`.
755
6839
        pub(crate) fn check_cert_type(self, desired_type: CertType) -> Result<Self> {
756
6839
            if self.0.peek_cert_type() != desired_type {
757
2
                return Err(EK::BadObjectVal.at_pos(self.1).with_msg(format!(
758
2
                    "bad certificate type {} (wanted {})",
759
2
                    self.0.peek_cert_type(),
760
2
                    desired_type
761
2
                )));
762
6837
            }
763
6837
            Ok(self)
764
6839
        }
765
        /// Give an error if this certificate's subject_key is not `pk`
766
        #[cfg(feature = "routerdesc")]
767
2080
        pub(crate) fn check_subject_key_is(self, pk: &ed25519::Ed25519Identity) -> Result<Self> {
768
2080
            if self.0.peek_subject_key().as_ed25519() != Some(pk) {
769
2
                return Err(EK::BadObjectVal
770
2
                    .at_pos(self.1)
771
2
                    .with_msg("incorrect subject key"));
772
2078
            }
773
2078
            Ok(self)
774
2080
        }
775
        /// Consume this object and return the inner Ed25519 certificate.
776
6835
        pub(crate) fn into_unchecked(self) -> KeyUnknownCert {
777
6835
            self.0
778
6835
        }
779
    }
780
}
781

            
782
/// Digest identifeirs, and digests in the form `ALGORITHM=BASE64U`
783
///
784
/// As found in a vote's `m` line.
785
mod identified_digest {
786
    use super::*;
787

            
788
    define_derive_deftly! {
789
        /// impl `FromStr` and `Display` for an enum with unit variants but also "unknown"
790
        ///
791
        /// Expected input: an enum whose variants are either
792
        ///  * unit variants, perhaps with `#[deftly(string_repr = "string")]`
793
        ///  * singleton tuple variant, containing `String` (or near equivalent)
794
        ///
795
        /// If `#[deftly(string_repro)]` is not specified,
796
        /// the default is snake case of the variant name.
797
        //
798
        // This macro may seem overkill, but open-coding these impls gives opportunities
799
        // for mismatches between FromStr, Display, and the variant name.
800
        //
801
        // TODO consider putting this in tor-basic-utils (maybe with a better name),
802
        // or possibly asking if derive_more want their FromStr to have this.
803
        StringReprUnitsOrUnknown for enum, expect items, beta_deftly:
804

            
805
        ${define STRING_REPR {
806
            ${vmeta(string_repr)
807
              as str,
808
              default { ${concat ${snake_case $vname}} }
809
            }
810
        }}
811

            
812
        impl FromStr for $ttype {
813
            type Err = Void;
814
24
            fn from_str(s: &str) -> Result<Self, Void> {
815
                $(
816
                    ${when v_is_unit}
817
                    if s == $STRING_REPR {
818
                        return Ok($vtype)
819
                    }
820
                )
821
                $(
822
                    ${when not(v_is_unit)} // anything else had better be Unknown
823
                    // not using `return ..;` makes this a syntax error if there are several.
824
                    Ok($vtype { 0: s.into() })
825
                )
826
            }
827
        }
828
        impl Display for $ttype {
829
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
830
                let s: &str = match self {
831
                    $(
832
                        ${when v_is_unit}
833
                        $vtype => $STRING_REPR,
834
                    )
835
                    $(
836
                        ${when not(v_is_unit)}
837
                        $vpat => f_0,
838
                    )
839
                };
840
                Display::fmt(s, f)
841
            }
842
        }
843
    }
844

            
845
    /// The name of a digest algorithm.
846
    ///
847
    /// Can represent an unrecognised algorithm, so it's parsed and reproduced.
848
    #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
849
    #[derive_deftly(StringReprUnitsOrUnknown)]
850
    #[non_exhaustive]
851
    pub enum DigestName {
852
        /// SHA-256
853
        Sha256,
854
        /// Unknown
855
        Unknown(String),
856
    }
857

            
858
    /// A single digest made with a nominated digest algorithm, `ALGORITHM=DIGEST`
859
    #[derive(Debug, Clone, Eq, PartialEq, Hash, derive_more::Display)]
860
    #[display("{alg}={value}")]
861
    #[non_exhaustive]
862
    pub struct IdentifiedDigest {
863
        /// The algorithm name.
864
        alg: DigestName,
865

            
866
        /// The digest value.
867
        ///
868
        /// Invariant: length is correct for `alg`, assuming `alg` is known.
869
        value: B64,
870
    }
871

            
872
    impl NormalItemArgument for DigestName {}
873
    impl NormalItemArgument for IdentifiedDigest {}
874

            
875
    /// Invalid syntax parsing an `IdentifiedDigest`
876
    #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, thiserror::Error)]
877
    #[error("invalid syntax, espected ALGORITHM=DIGEST: {0}")]
878
    pub struct IdentifiedDigestParseError(String);
879

            
880
    impl FromStr for IdentifiedDigest {
881
        type Err = IdentifiedDigestParseError;
882

            
883
24
        fn from_str(s: &str) -> Result<Self, Self::Err> {
884
24
            (|| {
885
24
                let (alg, value) = s.split_once('=').ok_or("missing equals sign")?;
886

            
887
24
                let alg = alg.parse().void_unwrap();
888
24
                let value = value
889
24
                    .parse::<B64>()
890
24
                    .map_err(|e| format!("bad value: {}", e.report()))?;
891

            
892
24
                if let Some(exp_len) = (|| {
893
                    Some({
894
                        use DigestName::*;
895
24
                        match alg {
896
24
                            Sha256 => 32,
897
                            Unknown(_) => None?,
898
                        }
899
                    })
900
                })() {
901
24
                    let val_len = value.as_bytes().len();
902
24
                    if val_len != exp_len {
903
                        return Err(format!("got {val_len} bytes, expected {exp_len}"));
904
24
                    }
905
                }
906

            
907
24
                Ok(IdentifiedDigest { alg, value })
908
            })()
909
24
            .map_err(IdentifiedDigestParseError)
910
24
        }
911
    }
912
}
913

            
914
/// Types for decoding RSA fingerprints
915
mod fingerprint {
916
    use crate::{Error, NetdocErrorKind as EK, Pos, Result};
917
    use base64ct::{Base64Unpadded, Encoding as _};
918
    use std::fmt::{self, Display};
919
    use tor_llcrypto::pk::rsa::RsaIdentity;
920

            
921
    /// A hex-encoded RSA key identity (fingerprint) with spaces in it.
922
    ///
923
    /// Netdoc parsing adapter for [`RsaIdentity`]
924
    #[derive(Debug, Clone, Eq, PartialEq, derive_more::Deref, derive_more::Into)]
925
    #[allow(clippy::exhaustive_structs)]
926
    pub(crate) struct SpFingerprint(pub RsaIdentity);
927

            
928
    /// A hex-encoded fingerprint with no spaces.
929
    ///
930
    /// Netdoc parsing adapter for [`RsaIdentity`]
931
    #[derive(Debug, Clone, Eq, PartialEq, derive_more::Deref, derive_more::Into)]
932
    #[allow(clippy::exhaustive_structs)]
933
    pub struct Fingerprint(pub RsaIdentity);
934

            
935
    /// A base64-encoded fingerprint (unpadded)
936
    ///
937
    /// Netdoc parsing adapter for [`RsaIdentity`]
938
    #[derive(Debug, Clone, Eq, PartialEq, derive_more::Deref, derive_more::Into)]
939
    #[allow(clippy::exhaustive_structs)]
940
    pub struct Base64Fingerprint(pub RsaIdentity);
941

            
942
    /// A "long identity" in the format used for Family members.
943
    ///
944
    /// Netdoc parsing adapter for [`RsaIdentity`]
945
    #[derive(Debug, Clone, Eq, PartialEq, derive_more::Deref, derive_more::Into)]
946
    #[allow(clippy::exhaustive_structs)]
947
    pub(crate) struct LongIdent(pub RsaIdentity);
948

            
949
    /// Helper: parse an identity from a hexadecimal string
950
745984
    fn parse_hex_ident(s: &str) -> Result<RsaIdentity> {
951
746037
        RsaIdentity::from_hex(s).ok_or_else(|| {
952
1986
            EK::BadArgument
953
1986
                .at_pos(Pos::at(s))
954
1986
                .with_msg("wrong length on fingerprint")
955
1986
        })
956
745984
    }
957

            
958
    impl std::str::FromStr for SpFingerprint {
959
        type Err = Error;
960
2080
        fn from_str(s: &str) -> Result<SpFingerprint> {
961
2082
            let ident = parse_hex_ident(&s.replace(' ', "")).map_err(|e| e.at_pos(Pos::at(s)))?;
962
2076
            Ok(SpFingerprint(ident))
963
2080
        }
964
    }
965

            
966
    impl std::str::FromStr for Base64Fingerprint {
967
        type Err = Error;
968
1962
        fn from_str(s: &str) -> Result<Base64Fingerprint> {
969
1962
            let b = s.parse::<super::B64>()?;
970
1962
            let ident = RsaIdentity::from_bytes(b.as_bytes()).ok_or_else(|| {
971
                EK::BadArgument
972
                    .at_pos(Pos::at(s))
973
                    .with_msg("Wrong identity length")
974
            })?;
975
1962
            Ok(Base64Fingerprint(ident))
976
1962
        }
977
    }
978

            
979
    impl Display for Base64Fingerprint {
980
2
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
981
2
            Display::fmt(&Base64Unpadded::encode_string(self.as_bytes()), f)
982
2
        }
983
    }
984

            
985
    impl std::str::FromStr for Fingerprint {
986
        type Err = Error;
987
3516
        fn from_str(s: &str) -> Result<Fingerprint> {
988
3521
            let ident = parse_hex_ident(s).map_err(|e| e.at_pos(Pos::at(s)))?;
989
3506
            Ok(Fingerprint(ident))
990
3516
        }
991
    }
992

            
993
    impl Display for Fingerprint {
994
2
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
995
2
            Display::fmt(&hex::encode_upper(self.as_bytes()), f)
996
2
        }
997
    }
998

            
999
    impl std::str::FromStr for LongIdent {
        type Err = Error;
740388
        fn from_str(mut s: &str) -> Result<LongIdent> {
740388
            if s.starts_with('$') {
370
                s = &s[1..];
740172
            }
740388
            if let Some(idx) = s.find(['=', '~']) {
6
                s = &s[..idx];
740382
            }
740388
            let ident = parse_hex_ident(s)?;
738416
            Ok(LongIdent(ident))
740388
        }
    }
    impl crate::NormalItemArgument for Fingerprint {}
    impl crate::NormalItemArgument for Base64Fingerprint {}
}
/// A type for relay nicknames
mod nickname {
    use crate::{Error, NetdocErrorKind as EK, Pos, Result};
    use tinystr::TinyAsciiStr;
    /// This is a strange limit, but it comes from Tor.
    const MAX_NICKNAME_LEN: usize = 19;
    /// The nickname for a Tor relay.
    ///
    /// These nicknames are legacy mechanism that's occasionally useful in
    /// debugging. They should *never* be used to uniquely identify relays;
    /// nothing prevents two relays from having the same nickname.
    ///
    /// Nicknames are required to be ASCII, alphanumeric, and between 1 and 19
    /// characters inclusive.
    #[derive(Clone, Debug)]
    pub struct Nickname(tinystr::TinyAsciiStr<MAX_NICKNAME_LEN>);
    impl Nickname {
        /// Return a view of this nickname as a string slice.
8
        pub(crate) fn as_str(&self) -> &str {
8
            self.0.as_str()
8
        }
    }
    impl std::fmt::Display for Nickname {
2
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2
            self.as_str().fmt(f)
2
        }
    }
    impl std::str::FromStr for Nickname {
        type Err = Error;
422383
        fn from_str(s: &str) -> Result<Self> {
422385
            let tiny = TinyAsciiStr::from_str(s).map_err(|_| {
4
                EK::BadArgument
4
                    .at_pos(Pos::at(s))
4
                    .with_msg("Invalid nickname")
6
            })?;
422379
            if tiny.is_ascii_alphanumeric() && !tiny.is_empty() {
422375
                Ok(Nickname(tiny))
            } else {
4
                Err(EK::BadArgument
4
                    .at_pos(Pos::at(s))
4
                    .with_msg("Invalid nickname"))
            }
422383
        }
    }
    impl crate::NormalItemArgument for Nickname {}
}
#[cfg(test)]
mod test {
    // @@ begin test lint list maintained by maint/add_warning @@
    #![allow(clippy::bool_assert_comparison)]
    #![allow(clippy::clone_on_copy)]
    #![allow(clippy::dbg_macro)]
    #![allow(clippy::mixed_attributes_style)]
    #![allow(clippy::print_stderr)]
    #![allow(clippy::print_stdout)]
    #![allow(clippy::single_char_pattern)]
    #![allow(clippy::unwrap_used)]
    #![allow(clippy::unchecked_time_subtraction)]
    #![allow(clippy::useless_vec)]
    #![allow(clippy::needless_pass_by_value)]
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
    use itertools::Itertools;
    use base64ct::Encoding;
    use super::*;
    use crate::{Pos, Result};
    /// Decode s as a multi-line base64 string, ignoring ascii whitespace.
    fn base64_decode_ignore_ws(s: &str) -> std::result::Result<Vec<u8>, base64ct::Error> {
        let mut s = s.to_string();
        s.retain(|c| !c.is_ascii_whitespace());
        base64ct::Base64::decode_vec(s.as_str())
    }
    #[test]
    fn base64() -> Result<()> {
        // Test parsing success:
        // Unpadded:
        assert_eq!("Mi43MTgyOA".parse::<B64>()?.as_bytes(), &b"2.71828"[..]);
        assert!("Mi43MTgyOA".parse::<B64>()?.check_len(7..8).is_ok());
        assert_eq!("Mg".parse::<B64>()?.as_bytes(), &b"2"[..]);
        assert!("Mg".parse::<B64>()?.check_len(1..2).is_ok());
        assert_eq!(
            "8J+NkvCfjZLwn42S8J+NkvCfjZLwn42S"
                .parse::<B64>()?
                .as_bytes(),
            "🍒🍒🍒🍒🍒🍒".as_bytes()
        );
        assert!(
            "8J+NkvCfjZLwn42S8J+NkvCfjZLwn42S"
                .parse::<B64>()?
                .check_len(24..25)
                .is_ok()
        );
        assert!(
            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz8="
                .parse::<B64>()?
                .check_len(32..33)
                .is_ok()
        );
        // Padded:
        assert_eq!("Mi43MTgyOA==".parse::<B64>()?.as_bytes(), &b"2.71828"[..]);
        assert!("Mi43MTgyOA==".parse::<B64>()?.check_len(7..8).is_ok());
        assert_eq!("Mg==".parse::<B64>()?.as_bytes(), &b"2"[..]);
        assert!("Mg==".parse::<B64>()?.check_len(1..2).is_ok());
        // Test parsing failures:
        // Invalid character.
        assert!("Mi43!!!!!!".parse::<B64>().is_err());
        // Invalid last character.
        assert!("Mi".parse::<B64>().is_err());
        assert!(
            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxaaaa"
                .parse::<B64>()
                .is_err()
        );
        // Invalid length.
        assert!("Mi43MTgyOA".parse::<B64>()?.check_len(8..).is_err());
        Ok(())
    }
    #[test]
    fn base64_lengths() -> Result<()> {
        assert_eq!("".parse::<B64>()?.as_bytes(), b"");
        assert!("=".parse::<B64>().is_err());
        assert!("==".parse::<B64>().is_err());
        assert!("B".parse::<B64>().is_err());
        assert!("B=".parse::<B64>().is_err());
        assert!("B==".parse::<B64>().is_err());
        assert!("Bg=".parse::<B64>().is_err());
        assert_eq!("Bg".parse::<B64>()?.as_bytes(), b"\x06");
        assert_eq!("Bg==".parse::<B64>()?.as_bytes(), b"\x06");
        assert_eq!("BCg".parse::<B64>()?.as_bytes(), b"\x04\x28");
        assert_eq!("BCg=".parse::<B64>()?.as_bytes(), b"\x04\x28");
        assert!("BCg==".parse::<B64>().is_err());
        assert_eq!("BCDE".parse::<B64>()?.as_bytes(), b"\x04\x20\xc4");
        assert!("BCDE=".parse::<B64>().is_err());
        assert!("BCDE==".parse::<B64>().is_err());
        Ok(())
    }
    #[test]
    fn base64_rev() {
        use base64ct::{Base64, Base64Unpadded};
        // Check that strings that we accept are precisely ones which
        // can be generated by either Base64 or Base64Unpadded
        for n in 0..=5 {
            for c_vec in std::iter::repeat_n("ACEQg/=".chars(), n).multi_cartesian_product() {
                let s: String = c_vec.into_iter().collect();
                #[allow(clippy::print_stderr)]
                let b = match s.parse::<B64>() {
                    Ok(b) => {
                        eprintln!("{:10} {:?}", &s, b.as_bytes());
                        b
                    }
                    Err(_) => {
                        eprintln!("{:10} Err", &s);
                        continue;
                    }
                };
                let b = b.as_bytes();
                let ep = Base64::encode_string(b);
                let eu = Base64Unpadded::encode_string(b);
                assert!(
                    s == ep || s == eu,
                    "{:?} decoded to {:?} giving neither {:?} nor {:?}",
                    s,
                    b,
                    ep,
                    eu
                );
            }
        }
    }
    #[test]
    fn base16() -> Result<()> {
        assert_eq!("332e313432".parse::<B16>()?.as_bytes(), &b"3.142"[..]);
        assert_eq!("332E313432".parse::<B16>()?.as_bytes(), &b"3.142"[..]);
        assert_eq!("332E3134".parse::<B16>()?.as_bytes(), &b"3.14"[..]);
        assert!("332E313".parse::<B16>().is_err());
        assert!("332G3134".parse::<B16>().is_err());
        Ok(())
    }
    #[test]
    fn curve25519() -> Result<()> {
        use tor_llcrypto::pk::curve25519::PublicKey;
        let k1 = "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz8=";
        let k2 = hex::decode("a69c2d8475d6f245c3d1ff5f13b50f62c38002ee2e8f9391c12a2608cc4a933f")
            .unwrap();
        let k2: &[u8; 32] = &k2[..].try_into().unwrap();
        let k1: PublicKey = k1.parse::<Curve25519Public>()?.into();
        assert_eq!(k1, (*k2).into());
        assert!(
            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz"
                .parse::<Curve25519Public>()
                .is_err()
        );
        assert!(
            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORSomCMxKkz"
                .parse::<Curve25519Public>()
                .is_err()
        );
        assert!(
            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5wSomCMxKkz"
                .parse::<Curve25519Public>()
                .is_err()
        );
        assert!(
            "ppwthHXW8kXD0f9fE7UPYsOAAu4ORwSomCMxKkz"
                .parse::<Curve25519Public>()
                .is_err()
        );
        Ok(())
    }
    #[test]
    fn ed25519() -> Result<()> {
        use tor_llcrypto::pk::ed25519::Ed25519Identity;
        let k1 = "WVIPQ8oArAqLY4XzkcpIOI6U8KsUJHBQhG8SC57qru0";
        let k2 = hex::decode("59520f43ca00ac0a8b6385f391ca48388e94f0ab14247050846f120b9eeaaeed")
            .unwrap();
        let k1: Ed25519Identity = k1.parse::<Ed25519Public>()?.into();
        assert_eq!(k1, Ed25519Identity::from_bytes(&k2).unwrap());
        assert!(
            "WVIPQ8oArAqLY4Xzk0!!!!8KsUJHBQhG8SC57qru"
                .parse::<Ed25519Public>()
                .is_err()
        );
        assert!(
            "WVIPQ8oArAqLY4XzkcpIU8KsUJHBQhG8SC57qru"
                .parse::<Ed25519Public>()
                .is_err()
        );
        assert!(
            "WVIPQ8oArAqLY4XzkcpIU8KsUJHBQhG8SC57qr"
                .parse::<Ed25519Public>()
                .is_err()
        );
        // right length, bad key:
        assert!(
            "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxaaaa"
                .parse::<Curve25519Public>()
                .is_err()
        );
        Ok(())
    }
    #[test]
    fn time() -> Result<()> {
        use humantime::parse_rfc3339;
        use std::time::SystemTime;
        let t = "2020-09-29 13:36:33".parse::<Iso8601TimeSp>()?;
        let t: SystemTime = t.into();
        assert_eq!(t, parse_rfc3339("2020-09-29T13:36:33Z").unwrap());
        assert!("2020-FF-29 13:36:33".parse::<Iso8601TimeSp>().is_err());
        assert!("2020-09-29Q13:99:33".parse::<Iso8601TimeSp>().is_err());
        assert!("2020-09-29".parse::<Iso8601TimeSp>().is_err());
        assert!("too bad, waluigi time".parse::<Iso8601TimeSp>().is_err());
        assert_eq!(
            "2020-09-29 13:36:33",
            "2020-09-29 13:36:33".parse::<Iso8601TimeSp>()?.to_string()
        );
        let t = "2020-09-29T13:36:33".parse::<Iso8601TimeNoSp>()?;
        let t: SystemTime = t.into();
        assert_eq!(t, parse_rfc3339("2020-09-29T13:36:33Z").unwrap());
        assert!("2020-09-29 13:36:33".parse::<Iso8601TimeNoSp>().is_err());
        assert!("2020-09-29Q13:99:33".parse::<Iso8601TimeNoSp>().is_err());
        assert!("2020-09-29".parse::<Iso8601TimeNoSp>().is_err());
        assert!("too bad, waluigi time".parse::<Iso8601TimeNoSp>().is_err());
        assert_eq!(
            "2020-09-29T13:36:33",
            "2020-09-29T13:36:33"
                .parse::<Iso8601TimeNoSp>()?
                .to_string()
        );
        Ok(())
    }
    #[test]
    fn rsa_public_key() {
        // Taken from a chutney network.
        let key_b64 = r#"
        MIIBigKCAYEAsDkzTcKS4kAF56R2ijb9qCek53tKC1EwMdpWMk58bB28fY6kHc55
        E7n1hB+LC5neZlx88GKuZ9k8P3g0MlO5ejalcfBdIIm28Nz86JXf/L23YnEpxnG/
        IpxZEcmx/EYN+vwp72W3DGuzyntaoaut6lGJk+O/aRCLLcTm4MNznvN1ackK2H6b
        Xm2ejRwtVRLoPKODJiPGl43snCfXXWsMH3IALFOgm0szPLv2fAJzBI8VWrUN81M/
        lgwJhG6+xbr1CkrXI5fKs/TNr0B0ydC9BIZplmPrnXaeNklnw1cqUJ1oxDSgBrvx
        rpDo7paObjSPV26opa68QKGa7Gu2MZQC3RzViNCbawka/108g6hSUkoM+Om2oivr
        DvtMOs10MjsfibEBVnwEhqnlb/gj3hJkYoGRsCwAyMIaMObHcmAevMJRWAjGCc8T
        GMS9dSmg1IZst+U+V2OCcIHXT6wZ1zPsBM0pYKVLCwtewaq1306k0n+ekriEo7eI
        FS3Dd/Dx/a6jAgMBAAE=
        "#;
        let key_bytes = base64_decode_ignore_ws(key_b64).unwrap();
        let rsa = RsaPublicParse1Helper::from_vec(key_bytes, Pos::None).unwrap();
        let bits = tor_llcrypto::pk::rsa::PublicKey::from(rsa.clone()).bits();
        assert_eq!(bits, 3072);
        // tests on a valid key
        assert!(rsa.clone().check_exponent(65537).is_ok());
        assert!(rsa.clone().check_exponent(1337).is_err());
        assert!(rsa.clone().check_len_eq(3072).is_ok());
        assert!(rsa.clone().check_len(1024..=4096).is_ok());
        assert!(rsa.clone().check_len(1024..=1024).is_err());
        assert!(rsa.check_len(4096..).is_err());
        // A string of bytes that is not an RSA key.
        let failure = RsaPublicParse1Helper::from_vec(vec![1, 2, 3], Pos::None);
        assert!(failure.is_err());
    }
    #[cfg(feature = "routerdesc")]
    #[test]
    fn ed_cert() {
        use tor_llcrypto::pk::ed25519::Ed25519Identity;
        // From a chutney network.
        let cert_b64 = r#"
        AQQABwRNAR6m3kq5h8i3wwac+Ti293opoOP8RKGP9MT0WD4Bbz7YAQAgBACGCdys
        G7AwsoYMIKenDN6In6ReiGF8jaYoGqmWKDVBdGGMDIZyNIq+VdhgtAB1EyNFHJU1
        jGM0ir9dackL+PIsHbzJH8s/P/8RfUsKIL6/ZHbn3nKMxLH/8kjtxp5ScAA=
        "#;
        let cert_bytes = base64_decode_ignore_ws(cert_b64).unwrap();
        // From the cert above.
        let right_subject_key: Ed25519Identity = "HqbeSrmHyLfDBpz5OLb3eimg4/xEoY/0xPRYPgFvPtg"
            .parse::<Ed25519Public>()
            .unwrap()
            .into();
        // From `ed25519()` test above.
        let wrong_subject_key: Ed25519Identity = "WVIPQ8oArAqLY4XzkcpIOI6U8KsUJHBQhG8SC57qru0"
            .parse::<Ed25519Public>()
            .unwrap()
            .into();
        // decode and check correct type and key
        let cert = UnvalidatedEdCert::from_vec(cert_bytes, Pos::None)
            .unwrap()
            .check_cert_type(tor_cert::CertType::IDENTITY_V_SIGNING)
            .unwrap()
            .check_subject_key_is(&right_subject_key)
            .unwrap();
        // check wrong type.
        assert!(
            cert.clone()
                .check_cert_type(tor_cert::CertType::RSA_ID_X509)
                .is_err()
        );
        // check wrong key.
        assert!(cert.check_subject_key_is(&wrong_subject_key).is_err());
        // Try an invalid object that isn't a certificate.
        let failure = UnvalidatedEdCert::from_vec(vec![1, 2, 3], Pos::None);
        assert!(failure.is_err());
    }
    #[test]
    fn fingerprint() -> Result<()> {
        use tor_llcrypto::pk::rsa::RsaIdentity;
        let fp1 = "7467 A97D 19CD 2B4F 2BC0 388A A99C 5E67 710F 847E";
        let fp2 = "7467A97D19CD2B4F2BC0388AA99C5E67710F847E";
        let fp3 = "$7467A97D19CD2B4F2BC0388AA99C5E67710F847E";
        let fp4 = "$7467A97D19CD2B4F2BC0388AA99C5E67710F847E=fred";
        let k = hex::decode(fp2).unwrap();
        let k = RsaIdentity::from_bytes(&k[..]).unwrap();
        assert_eq!(RsaIdentity::from(fp1.parse::<SpFingerprint>()?), k);
        assert_eq!(RsaIdentity::from(fp2.parse::<SpFingerprint>()?), k);
        assert!(fp3.parse::<SpFingerprint>().is_err());
        assert!(fp4.parse::<SpFingerprint>().is_err());
        assert!(fp1.parse::<Fingerprint>().is_err());
        assert_eq!(RsaIdentity::from(fp2.parse::<Fingerprint>()?), k);
        assert!(fp3.parse::<Fingerprint>().is_err());
        assert!(fp4.parse::<Fingerprint>().is_err());
        assert_eq!(Fingerprint(k).to_string(), fp2);
        assert!(fp1.parse::<LongIdent>().is_err());
        assert_eq!(RsaIdentity::from(fp2.parse::<LongIdent>()?), k);
        assert_eq!(RsaIdentity::from(fp3.parse::<LongIdent>()?), k);
        assert_eq!(RsaIdentity::from(fp4.parse::<LongIdent>()?), k);
        assert!("xxxx".parse::<Fingerprint>().is_err());
        assert!("ffffffffff".parse::<Fingerprint>().is_err());
        let fp_b64 = "dGepfRnNK08rwDiKqZxeZ3EPhH4";
        assert_eq!(RsaIdentity::from(fp_b64.parse::<Base64Fingerprint>()?), k);
        assert_eq!(Base64Fingerprint(k).to_string(), fp_b64);
        Ok(())
    }
    #[test]
    fn nickname() -> Result<()> {
        let n: Nickname = "Foo".parse()?;
        assert_eq!(n.as_str(), "Foo");
        assert_eq!(n.to_string(), "Foo");
        let word = "Untr1gonometr1cally";
        assert_eq!(word.len(), 19);
        let long: Nickname = word.parse()?;
        assert_eq!(long.as_str(), word);
        let too_long = "abcdefghijklmnopqrstuvwxyz";
        let not_ascii = "Eyjafjallajökull";
        let too_short = "";
        let other_invalid = "contains space";
        assert!(not_ascii.len() <= 19);
        assert!(too_long.parse::<Nickname>().is_err());
        assert!(not_ascii.parse::<Nickname>().is_err());
        assert!(too_short.parse::<Nickname>().is_err());
        assert!(other_invalid.parse::<Nickname>().is_err());
        Ok(())
    }
}