1pub 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 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 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
70pub(crate) trait FromBytes: Sized {
74 fn from_bytes(b: &[u8], p: crate::Pos) -> crate::Result<Self>;
76 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 Transparent beta_deftly:
89
90 ${define TRANSPARENT_DOCS_IMPLS {
94 }}
97
98 ${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 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 $TRANSPARENT_DOCS_IMPLS
160 Transparent for struct, beta_deftly:
172
173 $TRANSPARENT_IMPLS
174}
175
176define_derive_deftly! {
177 use Transparent;
178
179 ${TRANSPARENT_DOCS_IMPLS}
192 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 $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 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
260mod 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 #[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 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 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 #[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
358mod b16impl {
362 use super::*;
363 use crate::{Error, NetdocErrorKind as EK, Pos, Result};
364
365 #[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 #[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 #[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 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 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
462mod curve25519impl {
466 use super::*;
467
468 use crate::{Error, NormalItemArgument, Result, types::misc::FixedB64};
469 use tor_llcrypto::pk::curve25519::PublicKey;
470
471 #[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
495mod 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 #[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 #[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,
538 }
539
540 impl NormalItemArgument for Ed25519AlgorithmString {}
541
542 #[derive(Debug, Clone, PartialEq, Eq, Deftly)]
553 #[derive_deftly(ItemValueEncodable, ItemValueParseable)]
554 #[non_exhaustive]
555 pub struct Ed25519IdentityLine {
556 pub alg: Ed25519AlgorithmString,
558
559 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
585mod ignored_impl {
589 use super::*;
590
591 use crate::parse2::ErrorProblem as EP;
592 use ArgumentError as AE;
593
594 #[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 #[derive(Debug, Clone, Deftly)]
657 #[non_exhaustive]
658 #[derive_deftly(ItemValueParseable, NetdocParseableFields)]
659 pub struct NotPresentEachValue;
660
661 #[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 pub struct IgnoredItemOrObjectValue(Void);
689
690 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Default)]
696 #[allow(clippy::exhaustive_structs)]
697 pub struct NoMoreArguments;
698
699 #[derive(Debug, Copy, Clone, Default, Ord, PartialOrd, Eq, PartialEq, Hash)]
754 #[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 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 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#[derive(Debug, PartialEq, Clone, Copy, Hash)]
981#[allow(clippy::exhaustive_enums)] pub enum Unknown<T> {
983 Discarded(PhantomData<T>),
985
986 #[cfg(feature = "retain-unknown")]
988 Retained(T),
989}
990
991impl<T> Unknown<T> {
992 pub fn new_discard() -> Self {
994 Unknown::Discarded(PhantomData)
995 }
996
997 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 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 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 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 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 #[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 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#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] #[derive(derive_more::Deref, derive_more::Into, derive_more::Display)]
1115pub struct F64Finite(f64);
1116
1117#[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 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#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
1139#[non_exhaustive]
1140pub enum F64FiniteParseError {
1141 #[error("syntax error")]
1143 Syntax(#[from] std::num::ParseFloatError),
1144
1145 #[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#[derive(Debug, PartialEq, Clone, Hash)]
1187#[allow(clippy::exhaustive_enums)] pub enum KeywordOrString<T: Copy> {
1189 Known(T),
1191
1192 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#[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
1234mod 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 #[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 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 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 #[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 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
1329mod 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 pub(crate) const RSA_FIXED_EXPONENT: u32 = 65537;
1341
1342 pub(crate) const RSA_MIN_BITS: usize = 1024;
1346
1347 #[allow(non_camel_case_types)]
1352 #[derive(Clone, Debug)]
1353 pub(crate) struct RsaPublicParse1Helper(PublicKey, Pos);
1354
1355 #[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 #[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 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 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 pub(crate) fn check_len_eq(self, n: usize) -> Result<Self> {
1418 self.check_len(n..=n)
1419 }
1420 }
1421
1422 impl RsaSha1Signature {
1423 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
1498mod 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 #[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 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 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 pub(crate) fn into_unchecked(self) -> KeyUnknownCert {
1559 self.0
1560 }
1561 }
1562
1563 #[derive(Debug, Clone, PartialEq, Eq)]
1570 #[allow(clippy::exhaustive_structs)]
1571 pub struct Ed25519IdentityCert {
1572 pub id_ed25519: ed25519::Ed25519Identity,
1574 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 pub fn verify(cert: KeyUnknownCert) -> StdResult<TimerangeBound<Self>, VerifyFailed> {
1593 let cert = cert
1594 .should_have_signing_key()
1596 .map_err(|_| VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData))?
1597 .check_signature()?
1599 .dangerously_assume_timely();
1602
1603 if cert.cert_type() != CertType::IDENTITY_V_SIGNING {
1605 return Err(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData));
1606 }
1607
1608 let id_ed25519 = *cert.signing_key().ok_or(VerifyFailed::Bug)?;
1610
1611 let sign_ed25519 = *cert
1613 .subject_key()
1614 .as_ed25519()
1615 .ok_or(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData))?;
1616
1617 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 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 #[derive(Debug, Clone, PartialEq, Eq)]
1671 #[allow(clippy::exhaustive_structs)]
1672 pub struct Ed25519FamilyCert {
1673 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 pub fn verify(
1699 id_ed25519: ed25519::Ed25519Identity,
1700 cert: KeyUnknownCert,
1701 ) -> StdResult<TimerangeBound<Self>, VerifyFailed> {
1702 let cert = cert
1703 .should_have_signing_key()?
1705 .check_signature()?
1707 .dangerously_assume_timely();
1710
1711 if cert.cert_type() != CertType::FAMILY_V_IDENTITY {
1713 return Err(ErrorProblem::ObjectInvalidData.into());
1714 }
1715
1716 let family_ed25519 = *cert.signing_key().ok_or(VerifyFailed::Bug)?;
1718
1719 let certified_key = *cert
1721 .subject_key()
1722 .as_ed25519()
1723 .ok_or(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData))?;
1724
1725 if certified_key != id_ed25519 {
1727 return Err(VerifyFailed::VerifyFailed);
1728 }
1729
1730 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 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 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1797 #[non_exhaustive]
1798 pub struct Ed25519NtorCrossCert {
1799 _promise_we_verified: (),
1802 }
1803
1804 impl EmbeddableCertObject<KeyUnknownCert> for Ed25519NtorCrossCert {
1805 const LABEL: &str = "ED25519 CERT";
1806 }
1807
1808 impl Ed25519NtorCrossCert {
1809 pub fn verify(
1822 ntor_ed25519: ed25519::Ed25519Identity,
1823 id_ed25519: ed25519::Ed25519Identity,
1824 cert: KeyUnknownCert,
1825 ) -> StdResult<TimerangeBound<Self>, VerifyFailed> {
1826 Ok(
1827 Self::verify_inner(ntor_ed25519, id_ed25519, cert)?
1829 .0
1830 .check_signature()?,
1832 )
1833 }
1834
1835 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 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 if cert.peek_cert_type() != CertType::NTOR_CC_IDENTITY {
1895 return Err(ErrorProblem::ObjectInvalidData.into());
1896 }
1897
1898 if cert.peek_subject_key() != &CertifiedKey::Ed25519(id_ed25519) {
1901 return Err(VerifyFailed::VerifyFailed);
1902 }
1903
1904 let (cert, sig) = cert
1912 .should_be_signed_with(&ntor_ed25519)?
1913 .dangerously_split()?;
1914
1915 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 pub(crate) fn dangerous_new_unverified() -> Self {
1943 Self {
1944 _promise_we_verified: (),
1945 }
1946 }
1947 }
1948}
1949
1950mod identified_digest {
1955 use super::*;
1956
1957 define_derive_deftly! {
1958 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)} 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 #[derive(Debug, Clone, Eq, PartialEq, Hash, Deftly)]
2023 #[derive_deftly(StringReprUnitsOrUnknown)]
2024 #[non_exhaustive]
2025 pub enum DigestName {
2026 Sha256,
2028 Unknown(String),
2030 }
2031
2032 #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, derive_more::Display)]
2034 #[display("{alg}={value}")]
2035 #[non_exhaustive]
2036 pub struct IdentifiedDigest {
2037 alg: DigestName,
2039
2040 value: B64,
2044 }
2045
2046 impl NormalItemArgument for DigestName {}
2047 impl NormalItemArgument for IdentifiedDigest {}
2048
2049 #[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
2099mod 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 #[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 #[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 #[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 #[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 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 let fp = args.take(10).collect::<Vec<_>>();
2166
2167 if fp.len() < 10 {
2169 return Err(ArgumentError::Missing);
2170 }
2171
2172 debug_assert_eq!(fp.len(), 10);
2174
2175 if fp.iter().any(|arg| arg.len() != 4) {
2177 return Err(ArgumentError::Invalid);
2178 }
2179
2180 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 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
2257mod nickname {
2259 use super::*;
2260 use tinystr::TinyAsciiStr;
2261
2262 const MAX_NICKNAME_LEN: usize = 19;
2264
2265 #[derive(Clone, Debug, PartialEq, Eq)]
2274 pub struct Nickname(tinystr::TinyAsciiStr<MAX_NICKNAME_LEN>);
2275
2276 #[derive(Clone, Debug, thiserror::Error)]
2278 #[error("invalid nickname")]
2279 #[non_exhaustive]
2280 pub struct InvalidNickname {}
2281
2282 impl Nickname {
2283 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
2312mod hostname {
2316 use super::*;
2317 use std::net::IpAddr;
2318
2319 #[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] #[derive(derive_more::Into, derive_more::Deref, derive_more::AsRef, derive_more::Display)]
2345 pub struct Hostname(String);
2346
2347 #[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] #[derive(derive_more::Display)]
2363 #[allow(clippy::exhaustive_enums)]
2364 pub enum InternetHost {
2366 #[display("{_0}")]
2368 Name(Hostname),
2369 #[display("{_0}")]
2371 IpAddr(IpAddr),
2372 }
2373
2374 #[derive(Clone, Debug, thiserror::Error)]
2376 #[error("invalid hostname")]
2377 #[non_exhaustive]
2378 pub struct InvalidHostname {}
2379
2380 #[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 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 !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 Err(InvalidInternetHost {})
2434 }
2435 }
2436 }
2437
2438 impl NormalItemArgument for Hostname {}
2439 impl NormalItemArgument for InternetHost {}
2440}
2441
2442mod contact_info {
2444 use super::*;
2445
2446 #[derive(Clone, Debug, PartialEq, Eq, Deftly)] #[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 #[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 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
2489mod 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 #[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
2529pub 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 #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, strum::EnumString, strum::Display)]
2542 #[non_exhaustive]
2543 pub enum OverloadGeneralVersion {
2544 #[strum(serialize = "1")]
2546 V1,
2547 }
2548
2549 impl NormalItemArgument for OverloadGeneralVersion {}
2550
2551 #[derive(Debug, Clone, Copy, PartialEq, Eq, Deftly)]
2555 #[derive_deftly(ItemValueParseable, ItemValueEncodable)]
2556 #[non_exhaustive]
2557 pub struct OverloadGeneral {
2558 pub version: OverloadGeneralVersion,
2560 pub since: Iso8601TimeSp,
2562 }
2563
2564 #[derive(Clone, Debug, PartialEq, Eq, Deftly)]
2568 #[derive_deftly(ItemValueParseable, ItemValueEncodable)]
2569 #[non_exhaustive]
2570 pub struct RouterDescIntroItem {
2571 pub nickname: Nickname,
2573
2574 pub address: std::net::Ipv4Addr,
2576
2577 pub orport: u16,
2579
2580 pub socksport: u16,
2582
2583 pub dirport: u16,
2585 }
2586
2587 #[derive(Clone, Debug, PartialEq, Eq, Deftly)]
2591 #[derive_deftly(ItemValueParseable, ItemValueEncodable)]
2592 #[non_exhaustive]
2593 pub struct ExtraInfoDigests {
2594 pub sha1: FixedB16U<20>,
2596
2597 pub sha2: Option<FixedB64<32>>,
2599 }
2600
2601 #[derive(Debug, Clone, Default, Deftly)]
2603 #[derive_deftly(AsMutSelf)]
2604 #[allow(clippy::exhaustive_structs)]
2605 pub struct RouterHashAccu {
2606 pub sha1: Option<[u8; 20]>,
2608 pub sha256: Option<[u8; 32]>,
2610 }
2611
2612 #[derive(Debug, Clone, PartialEq, Eq, Deftly)]
2614 #[derive_deftly(ItemValueEncodable)]
2615 #[allow(clippy::exhaustive_structs)]
2616 pub struct RouterSigEd25519(pub ed25519::Signature);
2619
2620 impl RouterSigEd25519 {
2621 const HASH_PREFIX_MAGIC: &str = "Tor router descriptor signature v1";
2623
2624 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 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 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 #[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 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 #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Deftly)]
2766 #[derive_deftly(ItemValueParseable, ItemValueEncodable)]
2767 #[non_exhaustive]
2768 pub struct Bandwidth {
2769 pub average: u64,
2771
2772 pub burst: u64,
2774
2775 pub observed: u64,
2777 }
2778
2779 #[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 pub bit: NumericBoolean,
2800
2801 #[deftly(netdoc(object))]
2803 pub cert: EmbeddedCert<Ed25519NtorCrossCert, KeyUnknownCert>,
2804 }
2805}
2806
2807#[cfg(test)]
2808mod test {
2809 #![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)] 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 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 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 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 assert!("Mi43!!!!!!".parse::<B64>().is_err());
2889 assert!("Mi".parse::<B64>().is_err());
2891 assert!(
2892 "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxaaaa"
2893 .parse::<B64>()
2894 .is_err()
2895 );
2896 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 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 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 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 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 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 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 let right_subject_key: Ed25519Identity = "HqbeSrmHyLfDBpz5OLb3eimg4/xEoY/0xPRYPgFvPtg"
3133 .parse::<Ed25519Public>()
3134 .unwrap()
3135 .into();
3136 let wrong_subject_key: Ed25519Identity = "WVIPQ8oArAqLY4XzkcpIOI6U8KsUJHBQhG8SC57qru0"
3138 .parse::<Ed25519Public>()
3139 .unwrap()
3140 .into();
3141
3142 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 assert!(
3151 cert.clone()
3152 .check_cert_type(tor_cert::CertType::RSA_ID_X509)
3153 .is_err()
3154 );
3155 assert!(cert.check_subject_key_is(&wrong_subject_key).is_err());
3157
3158 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]
3226 fn hostname() {
3227 use std::net::IpAddr;
3228
3229 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 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 let chk_addr = |s: &str| {
3248 let _: InvalidHostname = s.parse::<Hostname>().expect_err(s);
3249 chk_either(s);
3250 };
3251
3252 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 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 chk_bad("1");
3277 chk_bad("127.0.0.023");
3278
3279 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"); }
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")?; Ok(())
3333 }
3334
3335 #[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 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"); 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 roundtrip_string(
3387 "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014",
3388 ); roundtrip_string(
3390 "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
3391 ); }
3393
3394 #[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 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 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 assert_eq!(
3432 parse2(&vec!["ABAB"; 10].join(" ")).unwrap(),
3433 SpFingerprint(RsaIdentity::from_bytes(&[0xAB; 20]).unwrap())
3434 );
3435
3436 assert_eq!(
3438 parse2(&vec!["ABAB"; 11].join(" ")).unwrap(),
3439 SpFingerprint(RsaIdentity::from_bytes(&[0xAB; 20]).unwrap())
3440 );
3441
3442 assert!(matches!(
3444 parse2(&vec!["ABAB"; 9].join(" ")).unwrap_err(),
3445 ErrorProblem::MissingArgument { .. }
3446 ));
3447
3448 assert!(matches!(
3453 parse2("0000 000000 00 0000 0000 0000 0000 0000 0000 0000").unwrap_err(),
3454 ErrorProblem::InvalidArgument { .. }
3455 ));
3456
3457 assert!(matches!(
3459 parse2(&vec!["ZZZZ"; 10].join(" ")).unwrap_err(),
3460 ErrorProblem::InvalidArgument { .. }
3461 ));
3462 }
3463
3464 #[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 let tests = [
3481 ("intro\nfoo\n", Ok(true)),
3483 ("intro\n", Ok(false)),
3485 ("intro\nfoo\nfoo\n", Err(ErrorProblem::ItemRepeated)),
3487 ("intro\nbar\nfoo\nfoo\n", Err(ErrorProblem::ItemRepeated)),
3489 ("intro\nfoo bar\n", Ok(true)),
3491 ("intro\nfoo bar baz\n", Ok(true)),
3493 (
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 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 #[derive(Debug, Deftly)]
3545 #[derive_deftly(NetdocParseable)]
3546 #[allow(unused)]
3547 struct TestDoc {
3548 router: RouterDescIntroItem,
3550
3551 #[deftly(netdoc(single_arg))]
3553 published: Iso8601TimeSp,
3554
3555 #[deftly(netdoc(single_arg))]
3557 master_key_ed25519: Ed25519Public,
3558
3559 #[deftly(netdoc(single_arg))]
3561 ntor_onion_key: Curve25519Public,
3562
3563 ntor_onion_key_crosscert: NtorOnionKeyCrossCert,
3565 }
3566
3567 impl TestDoc {
3568 fn verify(&self, now: SystemTime) {
3570 Ed25519NtorCrossCert::verify(
3571 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 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 trait Ed25519CertTest: Sized + PartialEq + Eq + Debug {
3610 fn new(
3614 signing_key: ed25519::Ed25519Identity,
3615 certified_key: ed25519::Ed25519Identity,
3616 ) -> Self;
3617
3618 fn cert_type() -> CertType;
3620
3621 fn new_signed(
3626 signing_key: &ed25519::Keypair,
3627 certified_key: ed25519::Ed25519Identity,
3628 expiry: SystemTime,
3629 ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug>;
3630
3631 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 fn str_to_st(s: &str) -> SystemTime {
3736 Iso8601TimeSp::from_str(s).unwrap().0
3737 }
3738
3739 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 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 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 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 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 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 (
3807 T::cert_type(),
3808 now - Duration::from_secs(64 * 64 * 24),
3810 certified_pk.into(),
3811 Some(&signing_pk),
3812 &signing_key,
3813 ),
3814 (
3816 CertType::LINK_AUTH_X509,
3818 expiry,
3819 certified_pk.into(),
3820 Some(&signing_pk),
3821 &signing_key,
3822 ),
3823 (
3825 T::cert_type(),
3826 expiry,
3827 CertifiedKey::RsaSha256Digest(certified_pk.into()),
3829 Some(&signing_pk),
3830 &signing_key,
3831 ),
3832 ];
3837
3838 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 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}