1pub use b16impl::*;
7pub use b64impl::*;
8pub use contact_info::*;
9pub use curve25519impl::*;
10pub use ed25519impl::*;
11pub use edcert::*;
12pub use fingerprint::*;
13pub use hostname::*;
14pub use rsa::*;
15pub use timeimpl::*;
16
17pub use nickname::{InvalidNickname, Nickname};
18
19pub use boolean::NumericBoolean;
20
21pub use fingerprint::{Base64Fingerprint, Fingerprint};
22
23pub use identified_digest::{DigestName, IdentifiedDigest};
24
25pub use ignored_impl::{
26 Ignored, IgnoredItemOrObjectValue, ItemPresent, NoMoreArguments, NotPresent,
27 NotPresentEachValue,
28};
29
30use crate::NormalItemArgument;
31use crate::encode::{
32 self,
33 ItemArgument,
34 ItemEncoder,
35 ItemObjectEncodable,
36 ItemValueEncodable,
37 MultiplicitySelector as EMultiplicitySelector,
39 NetdocEncoder,
40};
41use crate::parse2::{
42 self, ArgumentError, ArgumentStream, ItemArgumentParseable, ItemObjectParseable,
43 ItemValueParseable, SignatureHashInputs, UnparsedItem,
44 multiplicity::{
45 ArgumentSetMethods,
46 ItemSetMethods,
47 MultiplicitySelector as P2MultiplicitySelector,
49 ObjectSetMethods,
50 },
51 sig_hashes::Sha1WholeKeywordLine,
52};
53
54use derive_deftly::{Deftly, define_derive_deftly, define_derive_deftly_module};
55use digest::Digest as _;
56use educe::Educe;
57use std::cmp::{self, Ordering, PartialOrd};
58use std::fmt::{self, Display};
59use std::iter;
60use std::marker::PhantomData;
61use std::ops::{Deref, DerefMut};
62use std::result::Result as StdResult;
63use std::str::FromStr;
64use subtle::{Choice, ConstantTimeEq};
65use tor_error::{Bug, ErrorReport as _, internal, into_internal};
66use void::{ResultVoidExt as _, Void};
67
68pub(crate) trait FromBytes: Sized {
72 fn from_bytes(b: &[u8], p: crate::Pos) -> crate::Result<Self>;
74 fn from_vec(v: Vec<u8>, p: crate::Pos) -> crate::Result<Self> {
77 Self::from_bytes(&v[..], p)
78 }
79}
80
81define_derive_deftly_module! {
82 Transparent beta_deftly:
87
88 ${define TRANSPARENT_DOCS_IMPLS {
92 }}
95
96 ${define TRANSPARENT_IMPLS {
98
99 ${for fields {
100 ${loop_exactly_1 "must be applied to a single-field struct"}
101
102 impl<$tgens> From<$ftype> for $ttype {
103 fn from($fpatname: $ftype) -> $ttype {
104 $vpat
105 }
106 }
107
108 impl<$tgens> From<$ttype> for $ftype {
112 fn from(self_: $ttype) -> $ftype {
113 self_.$fname
114 }
115 }
116
117 impl<$tgens> Deref for $ttype {
118 type Target = $ftype;
119 fn deref(&self) -> &$ftype {
120 &self.$fname
121 }
122 }
123
124 impl<$tgens> DerefMut for $ttype {
125 fn deref_mut(&mut self) -> &mut $ftype {
126 &mut self.$fname
127 }
128 }
129
130 impl<$tgens> AsRef<$ftype> for $ttype {
131 fn as_ref(&self) -> &$ftype {
132 &self.$fname
133 }
134 }
135
136 impl<$tgens> AsMut<$ftype> for $ttype {
137 fn as_mut(&mut self) -> &mut $ftype {
138 &mut self.$fname
139 }
140 }
141 }}
142 }}
143}
144
145define_derive_deftly! {
146 use Transparent;
147
148 $TRANSPARENT_DOCS_IMPLS
158 Transparent for struct, beta_deftly:
170
171 $TRANSPARENT_IMPLS
172}
173
174define_derive_deftly! {
175 use Transparent;
176
177 ${TRANSPARENT_DOCS_IMPLS}
190 BytesTransparent for struct, beta_deftly:
202
203 $TRANSPARENT_IMPLS
204
205 impl<$tgens> ConstantTimeEq for $ttype {
206 fn ct_eq(&self, other: &$ttype) -> Choice {
207 $(
208 self.$fname.ct_eq(&other.$fname)
209 )
210 }
211 }
212 $impl<$tgens> PartialEq for $ttype {
214 fn eq(&self, other: &$ttype) -> bool {
215 self.ct_eq(other).into()
216 }
217 }
218 impl<$tgens> Eq for $ttype {}
219 impl<$tgens> PartialOrd for $ttype {
220 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
221 Some(self.cmp(other))
222 }
223 }
224 impl<$tgens> Ord for $ttype {
225 fn cmp(&self, other: &Self) -> Ordering {
226 $(
227 self.$fname.cmp(&other.$fname)
228 )
229 }
230 }
231
232 impl<$tgens> $ttype {
233 pub fn as_bytes(&self) -> &[u8] {
235 $(
236 &self.$fname[..]
237 )
238 }
239 }
240
241 impl<$tgens> AsRef<[u8]> for $ttype {
242 fn as_ref(&self) -> &[u8] {
243 $(
244 self.$fname.as_ref()
245 )
246 }
247 }
248
249 impl<$tgens> AsMut<[u8]> for $ttype {
250 fn as_mut(&mut self) -> &mut [u8] {
251 $(
252 self.$fname.as_mut()
253 )
254 }
255 }
256}
257
258mod b64impl {
260 use super::*;
261 use crate::{Error, NetdocErrorKind as EK, Pos, Result};
262 use base64ct::{Base64, Base64Unpadded, Encoding};
263 use std::ops::RangeBounds;
264
265 #[derive(Clone, Hash, Deftly)]
269 #[derive_deftly(BytesTransparent)]
270 #[allow(clippy::derived_hash_with_manual_eq)]
271 #[derive(derive_more::Debug)]
272 #[debug(r#"B64("{self}")"#)]
273 #[allow(clippy::exhaustive_structs)]
274 pub struct B64(pub Vec<u8>);
275
276 impl FromStr for B64 {
277 type Err = Error;
278 fn from_str(s: &str) -> Result<Self> {
279 let v: core::result::Result<Vec<u8>, base64ct::Error> = match s.len() % 4 {
280 0 => Base64::decode_vec(s),
281 _ => Base64Unpadded::decode_vec(s),
282 };
283 let v = v.map_err(|_| {
284 EK::BadArgument
285 .with_msg("Invalid base64")
286 .at_pos(Pos::at(s))
287 })?;
288 Ok(B64(v))
289 }
290 }
291
292 impl Display for B64 {
293 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
294 Display::fmt(&Base64Unpadded::encode_string(&self.0), f)
295 }
296 }
297
298 impl B64 {
299 pub(crate) fn check_len<B: RangeBounds<usize>>(self, bounds: B) -> Result<Self> {
302 if bounds.contains(&self.0.len()) {
303 Ok(self)
304 } else {
305 Err(EK::BadObjectVal.with_msg("Invalid length on base64 data"))
306 }
307 }
308
309 pub(crate) fn into_array<const N: usize>(self) -> Result<[u8; N]> {
313 self.0
314 .try_into()
315 .map_err(|_| EK::BadObjectVal.with_msg("Invalid length on base64 data"))
316 }
317 }
318
319 impl FromIterator<u8> for B64 {
320 fn from_iter<T: IntoIterator<Item = u8>>(iter: T) -> Self {
321 Self(iter.into_iter().collect())
322 }
323 }
324
325 impl NormalItemArgument for B64 {}
326
327 #[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, Hash, Deftly)]
473 #[derive_deftly(Transparent)]
475 #[allow(clippy::exhaustive_structs)]
476 pub struct Curve25519Public(pub PublicKey);
477
478 impl FromStr for Curve25519Public {
479 type Err = Error;
480 fn from_str(s: &str) -> Result<Self> {
481 let pk: FixedB64<32> = s.parse()?;
482 let pk: [u8; 32] = pk.into();
483 Ok(Curve25519Public(pk.into()))
484 }
485 }
486
487 impl Display for Curve25519Public {
488 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
489 FixedB64::from(self.0.to_bytes()).fmt(f)
490 }
491 }
492
493 impl NormalItemArgument for Curve25519Public {}
494}
495
496mod ed25519impl {
500 use super::*;
501
502 use crate::{Error, NormalItemArgument, Result, types::misc::FixedB64};
503 use derive_deftly::Deftly;
504 use tor_llcrypto::pk::ed25519::{Ed25519Identity, Signature};
505
506 #[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, Deftly)]
509 #[derive_deftly(Transparent)]
510 #[allow(clippy::exhaustive_structs)]
511 pub struct Ed25519Public(pub Ed25519Identity);
512
513 impl FromStr for Ed25519Public {
514 type Err = Error;
515 fn from_str(s: &str) -> Result<Self> {
516 let pk: FixedB64<32> = s.parse()?;
517 Ok(Ed25519Public(Ed25519Identity::new(pk.into())))
518 }
519 }
520
521 impl Display for Ed25519Public {
522 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
523 let pk: [u8; 32] = self.0.into();
524 let pk = FixedB64::from(pk);
525 pk.fmt(f)
526 }
527 }
528
529 impl NormalItemArgument for Ed25519Public {}
530
531 #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)] #[derive(derive_more::Display, derive_more::FromStr)]
534 #[display(rename_all = "lowercase")]
535 #[from_str(rename_all = "lowercase")]
536 #[allow(clippy::exhaustive_enums)]
537 pub enum Ed25519AlgorithmString {
538 Ed25519,
540 }
541
542 impl NormalItemArgument for Ed25519AlgorithmString {}
543
544 #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, Deftly)]
555 #[derive_deftly(ItemValueEncodable, ItemValueParseable)]
556 #[non_exhaustive]
557 pub struct Ed25519IdentityLine {
558 pub alg: Ed25519AlgorithmString,
560
561 pub pk: Ed25519Public,
563 }
564
565 impl From<Ed25519Public> for Ed25519IdentityLine {
566 fn from(pk: Ed25519Public) -> Self {
567 Self {
568 alg: Ed25519AlgorithmString::Ed25519,
569 pk,
570 }
571 }
572 }
573
574 impl From<Ed25519Identity> for Ed25519IdentityLine {
575 fn from(pk: Ed25519Identity) -> Self {
576 Ed25519Public(pk).into()
577 }
578 }
579
580 impl ItemArgument for Signature {
581 fn write_arg_onto(&self, out: &mut ItemEncoder) -> StdResult<(), Bug> {
582 FixedB64::from(self.to_bytes()).write_arg_onto(out)
583 }
584 }
585}
586
587mod ignored_impl {
591 use super::*;
592
593 use crate::parse2::ErrorProblem as EP;
594 use ArgumentError as AE;
595
596 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Default)]
626 #[allow(clippy::exhaustive_structs)]
627 #[derive(Deftly)]
628 #[derive_deftly(NetdocEncodableFields, NetdocParseableFields)]
629 pub struct NotPresent;
630
631 #[derive(Debug, Clone, Deftly)]
659 #[non_exhaustive]
660 #[derive_deftly(ItemValueParseable, NetdocParseableFields)]
661 pub struct NotPresentEachValue;
662
663 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Default, Deftly)]
678 #[derive_deftly(ItemValueParseable, NetdocParseableFields)]
679 #[allow(clippy::exhaustive_structs)]
680 pub struct Ignored;
681
682 pub struct IgnoredItemOrObjectValue(Void);
691
692 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Default)]
698 #[allow(clippy::exhaustive_structs)]
699 pub struct NoMoreArguments;
700
701 #[derive(Debug, Copy, Clone, Default, Ord, PartialOrd, Eq, PartialEq, Hash)]
756 #[derive(
758 derive_more::From,
759 derive_more::Deref,
760 derive_more::DerefMut,
761 derive_more::AsRef,
762 derive_more::AsMut,
763 )]
764 #[allow(clippy::exhaustive_structs)]
765 pub struct ItemPresent<T: Default>(pub T);
766
767 impl ItemSetMethods for P2MultiplicitySelector<NotPresent> {
768 type Each = NotPresentEachValue;
769 type Field = NotPresent;
770 fn can_accumulate(self, _acc: &Option<NotPresent>) -> Result<(), EP> {
771 Ok(())
772 }
773 fn accumulate(self, _: &mut Option<NotPresent>, _: NotPresentEachValue) -> Result<(), EP> {
774 Ok(())
775 }
776 fn finish(self, _acc: Option<NotPresent>, _: &'static str) -> Result<NotPresent, EP> {
777 Ok(NotPresent)
778 }
779 fn debug_core(self) -> &'static str {
780 "Ignored"
781 }
782 }
783
784 impl ItemValueEncodable for NotPresentEachValue {
785 fn write_item_value_onto(&self, _out: ItemEncoder) -> Result<(), Bug> {
786 Err(internal!("NotPresentEachValue as ItemValueEncodable"))
787 }
788 }
789
790 impl ArgumentSetMethods for P2MultiplicitySelector<NotPresent> {
791 type Each = NotPresentEachValue;
792 type Field = NotPresent;
793
794 fn parse_with<P>(self, _: &mut ArgumentStream<'_>, _: P) -> Result<Self::Field, AE>
795 where
796 P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result<Self::Each, AE>,
797 {
798 Ok(NotPresent)
799 }
800
801 fn debug_core(self) -> &'static str {
802 "NotPresent"
803 }
804 }
805 impl ItemArgument for NotPresentEachValue {
806 fn write_arg_onto(&self, _out: &mut ItemEncoder) -> Result<(), Bug> {
807 Err(internal!("NotPresentEachValue as ItemArgument"))
808 }
809 }
810 impl ItemArgumentParseable for NotPresentEachValue {
811 fn from_args<'s>(_: &mut ArgumentStream<'s>) -> Result<Self, ArgumentError> {
812 Err(AE::Unexpected)
814 }
815 }
816
817 impl ItemObjectEncodable for NotPresentEachValue {
818 fn label(&self) -> &str {
819 "INTERNAL ERROR"
820 }
821 fn write_object_onto(&self, _b: &mut Vec<u8>) -> Result<(), Bug> {
822 Err(internal!("NotPresentEachValue as ItemObjectEncodable"))
823 }
824 }
825
826 impl ObjectSetMethods for P2MultiplicitySelector<NotPresent> {
827 type Field = NotPresent;
828 type Each = NotPresentEachValue;
829 fn resolve_option(self, _found: Option<NotPresentEachValue>) -> Result<NotPresent, EP> {
830 Ok(NotPresent)
831 }
832 fn debug_core(self) -> &'static str {
833 "NotPresent"
834 }
835 }
836 impl ItemObjectParseable for NotPresentEachValue {
837 fn check_label(_label: &str) -> Result<(), EP> {
838 Err(EP::ObjectUnexpected)
839 }
840 fn from_bytes(_input: &[u8]) -> Result<Self, EP> {
841 Err(EP::ObjectUnexpected)
842 }
843 }
844
845 impl<'f> encode::MultiplicityMethods<'f> for EMultiplicitySelector<NotPresent> {
846 type Field = NotPresent;
847 type Each = NotPresentEachValue;
848 fn iter_ordered(self, _: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
849 iter::empty()
850 }
851 }
852
853 impl encode::OptionalityMethods for EMultiplicitySelector<NotPresent> {
854 type Field = NotPresent;
855 type Each = NotPresentEachValue;
856 fn as_option<'f>(self, _: &'f Self::Field) -> Option<&'f Self::Each> {
857 None
858 }
859 }
860
861 impl FromStr for Ignored {
862 type Err = Void;
863 fn from_str(_s: &str) -> Result<Ignored, Void> {
864 Ok(Ignored)
865 }
866 }
867
868 impl ItemArgumentParseable for Ignored {
869 fn from_args(_: &mut ArgumentStream) -> Result<Ignored, ArgumentError> {
870 Ok(Ignored)
871 }
872 }
873
874 impl ItemObjectParseable for Ignored {
875 fn check_label(_label: &str) -> Result<(), EP> {
876 Ok(())
878 }
879 fn from_bytes(_input: &[u8]) -> Result<Self, EP> {
880 Ok(Ignored)
881 }
882 }
883
884 impl ObjectSetMethods for P2MultiplicitySelector<Ignored> {
885 type Field = Ignored;
886 type Each = Ignored;
887 fn resolve_option(self, _found: Option<Ignored>) -> Result<Ignored, EP> {
888 Ok(Ignored)
889 }
890 fn debug_core(self) -> &'static str {
891 "Ignored"
892 }
893 }
894
895 impl<'f> encode::MultiplicityMethods<'f> for EMultiplicitySelector<Ignored> {
896 type Field = Ignored;
897 type Each = IgnoredItemOrObjectValue;
898 fn iter_ordered(self, _: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
899 iter::empty()
900 }
901 }
902
903 impl encode::OptionalityMethods for EMultiplicitySelector<Ignored> {
904 type Field = Ignored;
905 type Each = IgnoredItemOrObjectValue;
906 fn as_option<'f>(self, _: &'f Self::Field) -> Option<&'f Self::Each> {
907 None
908 }
909 }
910
911 impl ItemValueEncodable for IgnoredItemOrObjectValue {
912 fn write_item_value_onto(&self, _: ItemEncoder) -> Result<(), Bug> {
913 void::unreachable(self.0)
914 }
915 }
916
917 impl ItemObjectEncodable for IgnoredItemOrObjectValue {
918 fn label(&self) -> &str {
919 void::unreachable(self.0)
920 }
921 fn write_object_onto(&self, _: &mut Vec<u8>) -> Result<(), Bug> {
922 void::unreachable(self.0)
923 }
924 }
925
926 impl ItemArgumentParseable for NoMoreArguments {
927 fn from_args(args: &mut ArgumentStream) -> Result<NoMoreArguments, ArgumentError> {
928 Ok(args.reject_extra_args()?)
929 }
930 }
931
932 impl ItemArgument for NoMoreArguments {
933 fn write_arg_onto(&self, _: &mut ItemEncoder) -> Result<(), Bug> {
934 Ok(())
935 }
936 }
937
938 impl<T: Default> ItemValueParseable for ItemPresent<T> {
939 fn from_unparsed(item: UnparsedItem<'_>) -> StdResult<Self, EP> {
940 item.check_no_object()?;
941 Ok(Self::default())
942 }
943 }
944
945 impl<T: Default> ItemValueEncodable for ItemPresent<T> {
946 fn write_item_value_onto(&self, out: ItemEncoder) -> StdResult<(), Bug> {
947 out.finish();
948 Ok(())
949 }
950 }
951}
952
953#[derive(Debug, PartialEq, Clone, Copy, Hash)]
983#[allow(clippy::exhaustive_enums)] pub enum Unknown<T> {
985 Discarded(PhantomData<T>),
987
988 #[cfg(feature = "retain-unknown")]
990 Retained(T),
991}
992
993impl<T> Unknown<T> {
994 pub fn new_discard() -> Self {
996 Unknown::Discarded(PhantomData)
997 }
998
999 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Unknown<U> {
1001 self.try_map(move |t| Ok::<_, Void>(f(t))).void_unwrap()
1002 }
1003
1004 pub fn try_map<U, E>(self, f: impl FnOnce(T) -> Result<U, E>) -> Result<Unknown<U>, E> {
1006 Ok(match self {
1007 Unknown::Discarded(_) => Unknown::Discarded(PhantomData),
1008 #[cfg(feature = "retain-unknown")]
1009 Unknown::Retained(t) => Unknown::Retained(f(t)?),
1010 })
1011 }
1012
1013 pub fn as_ref(&self) -> Unknown<&T> {
1015 match self {
1016 Unknown::Discarded(_) => Unknown::Discarded(PhantomData),
1017 #[cfg(feature = "retain-unknown")]
1018 Unknown::Retained(t) => Unknown::Retained(t),
1019 }
1020 }
1021
1022 pub fn only_known(self) -> Option<T> {
1030 match self {
1031 Unknown::Discarded(_) => None,
1032 #[cfg(feature = "retain-unknown")]
1033 Unknown::Retained(t) => Some(t),
1034 }
1035 }
1036
1037 pub fn into_retained(self) -> Result<T, Bug> {
1041 match self {
1042 Unknown::Discarded(_) => Err(internal!("Unknown::retained but data not collected")),
1043 #[cfg(feature = "retain-unknown")]
1044 Unknown::Retained(t) => Ok(t),
1045 }
1046 }
1047
1048 #[cfg(feature = "retain-unknown")]
1050 pub fn new_retained_default() -> Self
1051 where
1052 T: Default,
1053 {
1054 Unknown::Retained(T::default())
1055 }
1056
1057 pub fn with_mut_unknown(&mut self, f: impl FnOnce(&mut T)) {
1065 match self {
1066 Unknown::Discarded(_) => {}
1067 #[cfg(feature = "retain-unknown")]
1068 Unknown::Retained(t) => f(t),
1069 }
1070 }
1071}
1072
1073impl<T: PartialOrd> PartialOrd for Unknown<T> {
1074 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
1075 use Unknown::*;
1076 match (self, other) {
1077 (Discarded(_), Discarded(_)) => Some(cmp::Ordering::Equal),
1078 #[cfg(feature = "retain-unknown")]
1079 (Discarded(_), Retained(_)) | (Retained(_), Discarded(_)) => None,
1080 #[cfg(feature = "retain-unknown")]
1081 (Retained(a), Retained(b)) => a.partial_cmp(b),
1082 }
1083 }
1084}
1085
1086#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] #[derive(derive_more::Deref, derive_more::Into, derive_more::Display)]
1123pub struct F64Finite(f64);
1124
1125#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error, amplify::Getters)]
1127#[error("FP value {} ({bits:#x}) is not finite", f64::from_bits(self.bits))]
1128pub struct F64FiniteError {
1129 bits: u64,
1133}
1134
1135impl TryFrom<f64> for F64Finite {
1136 type Error = F64FiniteError;
1137
1138 fn try_from(v: f64) -> Result<Self, F64FiniteError> {
1139 v.is_finite()
1140 .then_some(F64Finite(v))
1141 .ok_or_else(|| F64FiniteError { bits: v.to_bits() })
1142 }
1143}
1144
1145#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
1147#[non_exhaustive]
1148pub enum F64FiniteParseError {
1149 #[error("syntax error")]
1151 Syntax(#[from] std::num::ParseFloatError),
1152
1153 #[error("bad value")]
1155 NotFinite(#[from] F64FiniteError),
1156}
1157
1158impl FromStr for F64Finite {
1159 type Err = F64FiniteParseError;
1160
1161 fn from_str(s: &str) -> StdResult<Self, F64FiniteParseError> {
1162 Ok(s.parse::<f64>()?.try_into()?)
1163 }
1164}
1165
1166impl Eq for F64Finite {}
1167
1168#[allow(clippy::derive_ord_xor_partial_ord)]
1169impl Ord for F64Finite {
1170 fn cmp(&self, other: &F64Finite) -> cmp::Ordering {
1171 self.0
1172 .partial_cmp(&other.0)
1173 .expect("finite f64 partial_cmp gave None")
1174 }
1175}
1176
1177impl NormalItemArgument for F64Finite {}
1178
1179#[derive(Debug, PartialEq, Clone, Hash)]
1195#[allow(clippy::exhaustive_enums)] pub enum KeywordOrString<T: Copy> {
1197 Known(T),
1199
1200 Unknown(String),
1202}
1203
1204impl<T: Copy + NormalItemArgument> NormalItemArgument for KeywordOrString<T> {}
1205
1206impl<T: Copy + Display> Display for KeywordOrString<T> {
1207 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1208 match self {
1209 KeywordOrString::Known(t) => Display::fmt(t, f),
1210 KeywordOrString::Unknown(s) => Display::fmt(s, f),
1211 }
1212 }
1213}
1214
1215impl<T: Copy + FromStr> FromStr for KeywordOrString<T> {
1216 type Err = Void;
1217 fn from_str(s: &str) -> Result<Self, Void> {
1218 Ok(match s.parse() {
1219 Ok(y) => KeywordOrString::Known(y),
1220 Err(_) => KeywordOrString::Unknown(s.to_owned()),
1221 })
1222 }
1223}
1224
1225#[derive(Debug, Clone, Hash, Deftly, Eq, PartialEq, Ord, PartialOrd, Educe)]
1237#[educe(Default)]
1238#[derive_deftly(Transparent)]
1239#[allow(clippy::exhaustive_structs)]
1240pub struct RetainedOrderVec<T>(pub Vec<T>);
1241
1242mod timeimpl {
1246 use super::*;
1247 use crate::{Error, NetdocErrorKind as EK, Pos, Result};
1248 use std::time::SystemTime;
1249 use time::{
1250 OffsetDateTime, PrimitiveDateTime, format_description::FormatItem,
1251 macros::format_description,
1252 };
1253
1254 #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Deftly)]
1259 #[derive_deftly(Transparent)]
1260 #[allow(clippy::exhaustive_structs)]
1261 pub struct Iso8601TimeSp(pub SystemTime);
1262
1263 const ISO_8601SP_FMT: &[FormatItem] =
1265 format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
1266
1267 impl FromStr for Iso8601TimeSp {
1268 type Err = Error;
1269 fn from_str(s: &str) -> Result<Iso8601TimeSp> {
1270 let d = PrimitiveDateTime::parse(s, &ISO_8601SP_FMT).map_err(|e| {
1271 EK::BadArgument
1272 .at_pos(Pos::at(s))
1273 .with_msg(format!("invalid time: {}", e))
1274 })?;
1275 Ok(Iso8601TimeSp(d.assume_utc().into()))
1276 }
1277 }
1278
1279 fn fmt_with(
1284 t: SystemTime,
1285 format_desc: &[FormatItem],
1286 ) -> core::result::Result<String, fmt::Error> {
1287 OffsetDateTime::from(t)
1288 .format(format_desc)
1289 .map_err(|_| fmt::Error)
1290 }
1291
1292 impl Display for Iso8601TimeSp {
1293 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1294 write!(f, "{}", fmt_with(self.0, ISO_8601SP_FMT)?)
1295 }
1296 }
1297
1298 #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Deftly)]
1316 #[derive_deftly(Transparent)]
1317 #[allow(clippy::exhaustive_structs)]
1318 pub struct Iso8601TimeNoSp(pub SystemTime);
1319
1320 const ISO_8601NOSP_FMT: &[FormatItem] =
1322 format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]");
1323
1324 impl FromStr for Iso8601TimeNoSp {
1325 type Err = Error;
1326 fn from_str(s: &str) -> Result<Iso8601TimeNoSp> {
1327 let d = PrimitiveDateTime::parse(s, &ISO_8601NOSP_FMT).map_err(|e| {
1328 EK::BadArgument
1329 .at_pos(Pos::at(s))
1330 .with_msg(format!("invalid time: {}", e))
1331 })?;
1332 Ok(Iso8601TimeNoSp(d.assume_utc().into()))
1333 }
1334 }
1335
1336 impl Display for Iso8601TimeNoSp {
1337 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1338 write!(f, "{}", fmt_with(self.0, ISO_8601NOSP_FMT)?)
1339 }
1340 }
1341
1342 impl crate::NormalItemArgument for Iso8601TimeNoSp {}
1343}
1344
1345mod rsa {
1347 use super::*;
1348 use crate::{NetdocErrorKind as EK, Pos, Result};
1349 use std::ops::RangeBounds;
1350 use tor_llcrypto::pk::rsa::PublicKey;
1351 use tor_llcrypto::{d::Sha1, pk::rsa::KeyPair};
1352
1353 pub(crate) const RSA_FIXED_EXPONENT: u32 = 65537;
1357
1358 pub(crate) const RSA_MIN_BITS: usize = 1024;
1362
1363 #[allow(non_camel_case_types)]
1368 #[derive(Clone, Debug)]
1369 pub(crate) struct RsaPublicParse1Helper(PublicKey, Pos);
1370
1371 #[derive(Debug, Clone, PartialEq, Eq, Deftly)]
1388 #[derive_deftly(ItemValueParseable, ItemValueEncodable)]
1389 #[deftly(netdoc(no_extra_args, signature(hash_accu = Sha1WholeKeywordLine)))]
1390 #[non_exhaustive]
1391 pub struct RsaSha1Signature {
1392 #[deftly(netdoc(object(label = "SIGNATURE"), with = crate::types::raw_data_object))]
1394 pub signature: Vec<u8>,
1395 }
1396
1397 impl From<RsaPublicParse1Helper> for PublicKey {
1398 fn from(k: RsaPublicParse1Helper) -> PublicKey {
1399 k.0
1400 }
1401 }
1402 impl super::FromBytes for RsaPublicParse1Helper {
1403 fn from_bytes(b: &[u8], pos: Pos) -> Result<Self> {
1404 let key = PublicKey::from_der(b)
1405 .ok_or_else(|| EK::BadObjectVal.with_msg("unable to decode RSA public key"))?;
1406 Ok(RsaPublicParse1Helper(key, pos))
1407 }
1408 }
1409 impl RsaPublicParse1Helper {
1410 pub(crate) fn check_exponent(self, e: u32) -> Result<Self> {
1412 if self.0.exponent_is(e) {
1413 Ok(self)
1414 } else {
1415 Err(EK::BadObjectVal
1416 .at_pos(self.1)
1417 .with_msg("invalid RSA exponent"))
1418 }
1419 }
1420 pub(crate) fn check_len<B: RangeBounds<usize>>(self, bounds: B) -> Result<Self> {
1423 if bounds.contains(&self.0.bits()) {
1424 Ok(self)
1425 } else {
1426 Err(EK::BadObjectVal
1427 .at_pos(self.1)
1428 .with_msg("invalid RSA length"))
1429 }
1430 }
1431 pub(crate) fn check_len_eq(self, n: usize) -> Result<Self> {
1434 self.check_len(n..=n)
1435 }
1436 }
1437
1438 impl RsaSha1Signature {
1439 pub fn new_sign_netdoc(
1497 private_key: &KeyPair,
1498 encoder: &NetdocEncoder,
1499 item_keyword: &str,
1500 ) -> StdResult<Self, Bug> {
1501 let mut h = Sha1::new();
1502 h.update(encoder.text_sofar()?);
1503 h.update(item_keyword);
1504 h.update("\n");
1505 let h = h.finalize();
1506 let signature = private_key
1507 .sign(&h)
1508 .map_err(into_internal!("RSA signing failed"))?;
1509 Ok(RsaSha1Signature { signature })
1510 }
1511 }
1512}
1513
1514mod edcert {
1516 use std::result::Result as StdResult;
1517 use std::time::SystemTime;
1518
1519 use crate::types::EmbeddedCert;
1520 use crate::{
1521 NetdocErrorKind as EK, Pos, Result,
1522 parse2::{ErrorProblem, VerifyFailed},
1523 types::EmbeddableCertObject,
1524 };
1525 use tor_cert::{CertType, CertifiedKey, Ed25519Cert, KeyUnknownCert};
1526 use tor_checkable::signed::SignatureGated;
1527 use tor_checkable::timed::TimeRangeBound;
1528 use tor_checkable::{SelfSigned, TimeBound};
1529 use tor_error::{Bug, into_internal};
1530 use tor_llcrypto::pk::ed25519::{self, Ed25519PublicKey, ValidatableEd25519Signature};
1531
1532 #[derive(Debug, Clone)]
1535 pub(crate) struct UnvalidatedEdCert(KeyUnknownCert, Pos);
1536
1537 impl super::FromBytes for UnvalidatedEdCert {
1538 fn from_bytes(b: &[u8], p: Pos) -> Result<Self> {
1539 let cert = Ed25519Cert::decode(b).map_err(|e| {
1540 EK::BadObjectVal
1541 .at_pos(p)
1542 .with_msg("Bad certificate")
1543 .with_source(e)
1544 })?;
1545
1546 Ok(Self(cert, p))
1547 }
1548 fn from_vec(v: Vec<u8>, p: Pos) -> Result<Self> {
1549 Self::from_bytes(&v[..], p)
1550 }
1551 }
1552 impl UnvalidatedEdCert {
1553 pub(crate) fn check_cert_type(self, desired_type: CertType) -> Result<Self> {
1555 if self.0.peek_cert_type() != desired_type {
1556 return Err(EK::BadObjectVal.at_pos(self.1).with_msg(format!(
1557 "bad certificate type {} (wanted {})",
1558 self.0.peek_cert_type(),
1559 desired_type
1560 )));
1561 }
1562 Ok(self)
1563 }
1564 pub(crate) fn check_subject_key_is(self, pk: &ed25519::Ed25519Identity) -> Result<Self> {
1566 if self.0.peek_subject_key().as_ed25519() != Some(pk) {
1567 return Err(EK::BadObjectVal
1568 .at_pos(self.1)
1569 .with_msg("incorrect subject key"));
1570 }
1571 Ok(self)
1572 }
1573 pub(crate) fn into_unchecked(self) -> KeyUnknownCert {
1575 self.0
1576 }
1577 }
1578
1579 #[derive(Debug, Clone, PartialEq, Eq)]
1586 #[allow(clippy::exhaustive_structs)]
1587 pub struct Ed25519IdentityCert {
1588 pub id_ed25519: ed25519::Ed25519Identity,
1590 pub sign_ed25519: ed25519::Ed25519Identity,
1592 }
1593
1594 impl EmbeddableCertObject<KeyUnknownCert> for Ed25519IdentityCert {
1595 const LABEL: &str = "ED25519 CERT";
1596 }
1597
1598 impl Ed25519IdentityCert {
1599 pub fn verify(cert: KeyUnknownCert) -> StdResult<TimeRangeBound<Self>, VerifyFailed> {
1609 let cert = cert
1610 .should_have_signing_key()
1612 .map_err(|_| VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData))?
1613 .check_signature()?
1615 .dangerously_assume_timely();
1618
1619 if cert.cert_type() != CertType::IDENTITY_V_SIGNING {
1621 return Err(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData));
1622 }
1623
1624 let id_ed25519 = *cert.signing_key().ok_or(VerifyFailed::Bug)?;
1626
1627 let sign_ed25519 = *cert
1629 .subject_key()
1630 .as_ed25519()
1631 .ok_or(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData))?;
1632
1633 if ed25519::PublicKey::try_from(id_ed25519).is_err()
1637 || ed25519::PublicKey::try_from(sign_ed25519).is_err()
1638 {
1639 return Err(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData));
1640 }
1641
1642 Ok(TimeRangeBound::new(
1643 Self {
1644 id_ed25519,
1645 sign_ed25519,
1646 },
1647 ..cert.expiry(),
1648 ))
1649 }
1650
1651 pub fn new_signed(
1653 id_ed25519: &ed25519::Keypair,
1654 sign_ed25519: ed25519::Ed25519Identity,
1655 expiry: SystemTime,
1656 ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
1657 let cert = Ed25519Cert::builder()
1658 .expiration(expiry)
1659 .signing_key(id_ed25519.public_key().into())
1660 .cert_type(CertType::IDENTITY_V_SIGNING)
1661 .cert_key(sign_ed25519.into())
1662 .encode_and_sign(id_ed25519)
1663 .map_err(into_internal!("failed to encode and sign identity cert"))?;
1664
1665 let cert =
1666 Ed25519Cert::decode(&cert).map_err(into_internal!("decode just encoded cert"))?;
1667
1668 Ok(EmbeddedCert::new(
1669 Self {
1670 id_ed25519: id_ed25519.public_key().into(),
1671 sign_ed25519,
1672 },
1673 cert,
1674 ))
1675 }
1676 }
1677
1678 #[derive(Debug, Clone, PartialEq, Eq)]
1687 #[allow(clippy::exhaustive_structs)]
1688 pub struct Ed25519FamilyCert {
1689 pub family_ed25519: ed25519::Ed25519Identity,
1694 }
1695
1696 impl EmbeddableCertObject<KeyUnknownCert> for Ed25519FamilyCert {
1697 const LABEL: &str = "FAMILY CERT";
1698 }
1699
1700 impl Ed25519FamilyCert {
1701 pub fn verify(
1715 id_ed25519: ed25519::Ed25519Identity,
1716 cert: KeyUnknownCert,
1717 ) -> StdResult<TimeRangeBound<Self>, VerifyFailed> {
1718 let cert = cert
1719 .should_have_signing_key()?
1721 .check_signature()?
1723 .dangerously_assume_timely();
1726
1727 if cert.cert_type() != CertType::FAMILY_V_IDENTITY {
1729 return Err(ErrorProblem::ObjectInvalidData.into());
1730 }
1731
1732 let family_ed25519 = *cert.signing_key().ok_or(VerifyFailed::Bug)?;
1734
1735 let certified_key = *cert
1737 .subject_key()
1738 .as_ed25519()
1739 .ok_or(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData))?;
1740
1741 if certified_key != id_ed25519 {
1743 return Err(VerifyFailed::VerifyFailed);
1744 }
1745
1746 if ed25519::PublicKey::try_from(family_ed25519).is_err()
1748 || ed25519::PublicKey::try_from(id_ed25519).is_err()
1749 {
1750 return Err(VerifyFailed::ParseEmbedded(ErrorProblem::ObjectInvalidData));
1751 }
1752
1753 Ok(TimeRangeBound::new(
1754 Self { family_ed25519 },
1755 ..cert.expiry(),
1756 ))
1757 }
1758
1759 pub fn new_signed(
1761 family_ed25519: &ed25519::Keypair,
1762 id_ed25519: ed25519::Ed25519Identity,
1763 expiry: SystemTime,
1764 ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
1765 let cert = Ed25519Cert::builder()
1766 .expiration(expiry)
1767 .signing_key(family_ed25519.public_key().into())
1768 .cert_type(CertType::FAMILY_V_IDENTITY)
1769 .cert_key(id_ed25519.into())
1770 .encode_and_sign(family_ed25519)
1771 .map_err(into_internal!("failed to encode and sign family cert"))?;
1772
1773 let cert =
1774 Ed25519Cert::decode(&cert).map_err(into_internal!("decode just encoded cert"))?;
1775
1776 Ok(EmbeddedCert::new(
1777 Self {
1778 family_ed25519: family_ed25519.public_key().into(),
1779 },
1780 cert,
1781 ))
1782 }
1783 }
1784
1785 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1813 #[non_exhaustive]
1814 pub struct Ed25519NtorCrossCert {
1815 _promise_we_verified: (),
1818 }
1819
1820 impl EmbeddableCertObject<KeyUnknownCert> for Ed25519NtorCrossCert {
1821 const LABEL: &str = "ED25519 CERT";
1822 }
1823
1824 impl Ed25519NtorCrossCert {
1825 pub fn verify(
1838 ntor_ed25519: ed25519::Ed25519Identity,
1839 id_ed25519: ed25519::Ed25519Identity,
1840 cert: KeyUnknownCert,
1841 ) -> StdResult<TimeRangeBound<Self>, VerifyFailed> {
1842 Ok(
1843 Self::verify_inner(ntor_ed25519, id_ed25519, cert)?
1845 .0
1846 .check_signature()?,
1848 )
1849 }
1850
1851 pub fn new_signed(
1853 ntor_ed25519: &ed25519::ExpandedKeypair,
1854 id_ed25519: ed25519::Ed25519Identity,
1855 expiry: SystemTime,
1856 ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
1857 let cert = Ed25519Cert::builder()
1858 .expiration(expiry)
1859 .cert_type(CertType::NTOR_CC_IDENTITY)
1860 .cert_key(id_ed25519.into())
1861 .encode_and_sign(ntor_ed25519)
1862 .map_err(into_internal!("failed to encode and sign ntor cert"))?;
1863
1864 let cert =
1865 Ed25519Cert::decode(&cert).map_err(into_internal!("decode just encoded cert"))?;
1866
1867 Ok(EmbeddedCert::new(
1868 Self {
1869 _promise_we_verified: (),
1870 },
1871 cert,
1872 ))
1873 }
1874
1875 pub(crate) fn verify_inner(
1898 ntor_ed25519: ed25519::Ed25519Identity,
1899 id_ed25519: ed25519::Ed25519Identity,
1900 cert: KeyUnknownCert,
1901 ) -> StdResult<
1902 (
1903 SignatureGated<TimeRangeBound<Self>>,
1904 ValidatableEd25519Signature,
1905 SystemTime,
1906 ),
1907 VerifyFailed,
1908 > {
1909 if cert.peek_cert_type() != CertType::NTOR_CC_IDENTITY {
1911 return Err(ErrorProblem::ObjectInvalidData.into());
1912 }
1913
1914 if cert.peek_subject_key() != &CertifiedKey::Ed25519(id_ed25519) {
1917 return Err(VerifyFailed::VerifyFailed);
1918 }
1919
1920 let (cert, sig) = cert
1928 .should_be_signed_with(&ntor_ed25519)?
1929 .dangerously_split()?;
1930
1931 let cert = cert.dangerously_assume_timely();
1937 let expiration = ..cert.expiry();
1938
1939 Ok((
1940 SignatureGated::new(
1941 TimeRangeBound::new(
1942 Self {
1943 _promise_we_verified: (),
1944 },
1945 expiration,
1946 ),
1947 vec![Box::new(sig.clone())],
1948 ),
1949 sig,
1950 expiration.end,
1951 ))
1952 }
1953
1954 pub(crate) fn dangerous_new_unverified() -> Self {
1959 Self {
1960 _promise_we_verified: (),
1961 }
1962 }
1963 }
1964}
1965
1966mod identified_digest {
1971 use super::*;
1972
1973 define_derive_deftly! {
1974 StringReprUnitsOrUnknown for enum, expect items, beta_deftly:
1989
1990 ${define STRING_REPR {
1991 ${vmeta(string_repr)
1992 as str,
1993 default { ${concat ${snake_case $vname}} }
1994 }
1995 }}
1996
1997 impl FromStr for $ttype {
1998 type Err = Void;
1999 fn from_str(s: &str) -> Result<Self, Void> {
2000 $(
2001 ${when v_is_unit}
2002 if s == $STRING_REPR {
2003 return Ok($vtype)
2004 }
2005 )
2006 $(
2007 ${when not(v_is_unit)} Ok($vtype { 0: s.into() })
2010 )
2011 }
2012 }
2013 impl AsRef<str> for $ttype {
2014 fn as_ref(&self) -> &str {
2015 match self {
2016 $(
2017 ${when v_is_unit}
2018 $vtype => $STRING_REPR,
2019 )
2020 $(
2021 ${when not(v_is_unit)}
2022 $vpat => f_0,
2023 )
2024 }
2025 }
2026 }
2027 impl Display for $ttype {
2028 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2029 let s: &str = self.as_ref();
2030 Display::fmt(s, f)
2031 }
2032 }
2033 }
2034
2035 #[derive(Debug, Clone, Eq, PartialEq, Hash, Deftly)]
2039 #[derive_deftly(StringReprUnitsOrUnknown)]
2040 #[non_exhaustive]
2041 pub enum DigestName {
2042 Sha256,
2044 Unknown(String),
2046 }
2047
2048 #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, derive_more::Display)]
2050 #[display("{alg}={value}")]
2051 #[non_exhaustive]
2052 pub struct IdentifiedDigest {
2053 alg: DigestName,
2055
2056 value: B64,
2060 }
2061
2062 impl NormalItemArgument for DigestName {}
2063 impl NormalItemArgument for IdentifiedDigest {}
2064
2065 #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, thiserror::Error)]
2067 #[error("invalid syntax, expected ALGORITHM=DIGEST: {0}")]
2068 pub struct IdentifiedDigestParseError(String);
2069
2070 impl Ord for DigestName {
2071 fn cmp(&self, other: &Self) -> Ordering {
2072 self.as_ref().cmp(other.as_ref())
2073 }
2074 }
2075 impl PartialOrd for DigestName {
2076 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2077 Some(self.cmp(other))
2078 }
2079 }
2080
2081 impl FromStr for IdentifiedDigest {
2082 type Err = IdentifiedDigestParseError;
2083
2084 fn from_str(s: &str) -> Result<Self, Self::Err> {
2085 (|| {
2086 let (alg, value) = s.split_once('=').ok_or("missing equals sign")?;
2087
2088 let alg = alg.parse().void_unwrap();
2089 let value = value
2090 .parse::<B64>()
2091 .map_err(|e| format!("bad value: {}", e.report()))?;
2092
2093 if let Some(exp_len) = (|| {
2094 Some({
2095 use DigestName::*;
2096 match alg {
2097 Sha256 => 32,
2098 Unknown(_) => None?,
2099 }
2100 })
2101 })() {
2102 let val_len = value.as_bytes().len();
2103 if val_len != exp_len {
2104 return Err(format!("got {val_len} bytes, expected {exp_len}"));
2105 }
2106 }
2107
2108 Ok(IdentifiedDigest { alg, value })
2109 })()
2110 .map_err(IdentifiedDigestParseError)
2111 }
2112 }
2113}
2114
2115mod fingerprint {
2117 use super::*;
2118 use crate::parse2::{ArgumentError, ArgumentStream, ItemArgumentParseable};
2119 use crate::{Error, NetdocErrorKind as EK, Pos, Result};
2120 use base64ct::{Base64Unpadded, Encoding as _};
2121 use itertools::Itertools;
2122 use tor_llcrypto::pk::rsa::RsaIdentity;
2123
2124 #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
2130 #[derive_deftly(Transparent)]
2131 #[allow(clippy::exhaustive_structs)]
2132 pub struct SpFingerprint(pub RsaIdentity);
2133
2134 #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
2138 #[derive_deftly(Transparent)]
2139 #[allow(clippy::exhaustive_structs)]
2140 pub struct Fingerprint(pub RsaIdentity);
2141
2142 #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
2146 #[derive_deftly(Transparent)]
2147 #[allow(clippy::exhaustive_structs)]
2148 pub struct Base64Fingerprint(pub RsaIdentity);
2149
2150 #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
2154 #[derive_deftly(Transparent)]
2155 #[allow(clippy::exhaustive_structs)]
2156 pub(crate) struct LongIdent(pub RsaIdentity);
2157
2158 fn parse_hex_ident(s: &str) -> Result<RsaIdentity> {
2160 RsaIdentity::from_hex(s).ok_or_else(|| {
2161 EK::BadArgument
2162 .at_pos(Pos::at(s))
2163 .with_msg("wrong length on fingerprint")
2164 })
2165 }
2166
2167 impl FromStr for SpFingerprint {
2168 type Err = Error;
2169 fn from_str(s: &str) -> Result<SpFingerprint> {
2170 let ident = parse_hex_ident(&s.replace(' ', "")).map_err(|e| e.at_pos(Pos::at(s)))?;
2171 Ok(SpFingerprint(ident))
2172 }
2173 }
2174
2175 impl ItemArgumentParseable for SpFingerprint {
2176 fn from_args<'s>(
2177 args: &mut ArgumentStream<'s>,
2178 ) -> std::result::Result<Self, ArgumentError> {
2179 let fp = args.take(10).collect::<Vec<_>>();
2182
2183 if fp.len() < 10 {
2185 return Err(ArgumentError::Missing);
2186 }
2187
2188 debug_assert_eq!(fp.len(), 10);
2190
2191 if fp.iter().any(|arg| arg.len() != 4) {
2193 return Err(ArgumentError::Invalid);
2194 }
2195
2196 Ok(Self(
2199 RsaIdentity::from_hex(fp.join("").as_str()).ok_or(ArgumentError::Invalid)?,
2200 ))
2201 }
2202 }
2203
2204 impl encode::ItemArgument for SpFingerprint {
2205 fn write_arg_onto(&self, out: &mut ItemEncoder<'_>) -> StdResult<(), Bug> {
2206 let res = self
2207 .0
2208 .to_bytes()
2209 .chunks(2)
2210 .map(|b| format!("{:02X}{:02X}", b[0], b[1]))
2211 .join(" ");
2212 debug_assert_eq!(res.len(), 4 * 10 + 9);
2213 out.args_raw_string(&res);
2214 Ok(())
2215 }
2216 }
2217
2218 impl FromStr for Base64Fingerprint {
2219 type Err = Error;
2220 fn from_str(s: &str) -> Result<Base64Fingerprint> {
2221 let b = s.parse::<super::B64>()?;
2222 let ident = RsaIdentity::from_bytes(b.as_bytes()).ok_or_else(|| {
2223 EK::BadArgument
2224 .at_pos(Pos::at(s))
2225 .with_msg("Wrong identity length")
2226 })?;
2227 Ok(Base64Fingerprint(ident))
2228 }
2229 }
2230
2231 impl Display for Base64Fingerprint {
2232 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2233 Display::fmt(&Base64Unpadded::encode_string(self.as_bytes()), f)
2234 }
2235 }
2236
2237 impl FromStr for Fingerprint {
2238 type Err = Error;
2239 fn from_str(s: &str) -> Result<Fingerprint> {
2240 let ident = parse_hex_ident(s).map_err(|e| e.at_pos(Pos::at(s)))?;
2241 Ok(Fingerprint(ident))
2242 }
2243 }
2244
2245 impl Display for Fingerprint {
2246 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2247 Display::fmt(&hex::encode_upper(self.as_bytes()), f)
2248 }
2249 }
2250
2251 impl FromStr for LongIdent {
2252 type Err = Error;
2253 fn from_str(mut s: &str) -> Result<LongIdent> {
2254 s = s.strip_prefix('$').unwrap_or(s);
2255 s = s.split_once(['=', '~']).map(|(a, _)| a).unwrap_or(s);
2257 let ident = parse_hex_ident(s)?;
2258 Ok(LongIdent(ident))
2259 }
2260 }
2261
2262 impl Display for LongIdent {
2263 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2264 write!(f, "${}", self.0.as_hex_upper())
2265 }
2266 }
2267
2268 impl crate::NormalItemArgument for Fingerprint {}
2269 impl crate::NormalItemArgument for Base64Fingerprint {}
2270 impl crate::NormalItemArgument for LongIdent {}
2271}
2272
2273mod nickname {
2275 use super::*;
2276 use tinystr::TinyAsciiStr;
2277
2278 const MAX_NICKNAME_LEN: usize = 19;
2280
2281 #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
2290 pub struct Nickname(tinystr::TinyAsciiStr<MAX_NICKNAME_LEN>);
2291
2292 #[derive(Clone, Debug, thiserror::Error)]
2294 #[error("invalid nickname")]
2295 #[non_exhaustive]
2296 pub struct InvalidNickname {}
2297
2298 impl Nickname {
2299 pub fn as_str(&self) -> &str {
2301 self.0.as_str()
2302 }
2303 }
2304
2305 impl Display for Nickname {
2306 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2307 self.as_str().fmt(f)
2308 }
2309 }
2310
2311 impl FromStr for Nickname {
2312 type Err = InvalidNickname;
2313
2314 fn from_str(s: &str) -> Result<Self, InvalidNickname> {
2315 let tiny = TinyAsciiStr::from_str(s).map_err(|_| InvalidNickname {})?;
2316
2317 if tiny.is_ascii_alphanumeric() && !tiny.is_empty() {
2318 Ok(Nickname(tiny))
2319 } else {
2320 Err(InvalidNickname {})
2321 }
2322 }
2323 }
2324
2325 impl AsRef<str> for Nickname {
2326 fn as_ref(&self) -> &str {
2327 self.as_str()
2328 }
2329 }
2330
2331 impl crate::NormalItemArgument for Nickname {}
2332}
2333
2334mod hostname {
2338 use super::*;
2339 use std::net::IpAddr;
2340
2341 #[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] #[derive(derive_more::Into, derive_more::Deref, derive_more::AsRef, derive_more::Display)]
2367 pub struct Hostname(String);
2368
2369 #[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] #[derive(derive_more::Display)]
2385 #[allow(clippy::exhaustive_enums)]
2386 pub enum InternetHost {
2388 #[display("{_0}")]
2390 Name(Hostname),
2391 #[display("{_0}")]
2393 IpAddr(IpAddr),
2394 }
2395
2396 #[derive(Clone, Debug, thiserror::Error)]
2398 #[error("invalid hostname")]
2399 #[non_exhaustive]
2400 pub struct InvalidHostname {}
2401
2402 #[derive(Clone, Debug, thiserror::Error)]
2404 #[error("invalid: not a valid hostname, nor a valid IPv4 or IPv6 address")]
2405 #[non_exhaustive]
2406 pub struct InvalidInternetHost {}
2407
2408 impl Hostname {
2409 pub fn as_str(&self) -> &str {
2411 &self.0
2412 }
2413 }
2414
2415 impl AsRef<str> for Hostname {
2416 fn as_ref(&self) -> &str {
2417 self.as_str()
2418 }
2419 }
2420
2421 impl TryFrom<String> for Hostname {
2422 type Error = InvalidHostname;
2423 fn try_from(s: String) -> Result<Self, InvalidHostname> {
2424 if hostname_validator::is_valid(&s) &&
2425 !s.chars().all(|c| c.is_ascii_digit() || c == '.')
2429 {
2430 Ok(Hostname(s))
2431 } else {
2432 Err(InvalidHostname {})
2433 }
2434 }
2435 }
2436
2437 impl FromStr for Hostname {
2438 type Err = InvalidHostname;
2439 fn from_str(s: &str) -> Result<Self, InvalidHostname> {
2440 s.to_owned().try_into()
2441 }
2442 }
2443
2444 impl FromStr for InternetHost {
2445 type Err = InvalidInternetHost;
2446 fn from_str(s: &str) -> Result<Self, InvalidInternetHost> {
2447 if let Ok(y) = s.parse() {
2448 Ok(InternetHost::IpAddr(y))
2449 } else if let Ok(y) = s.parse() {
2450 Ok(InternetHost::Name(y))
2451 } else {
2452 Err(InvalidInternetHost {})
2456 }
2457 }
2458 }
2459
2460 impl NormalItemArgument for Hostname {}
2461 impl NormalItemArgument for InternetHost {}
2462}
2463
2464mod contact_info {
2466 use super::*;
2467
2468 #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Deftly)] #[derive(derive_more::Into, derive_more::AsRef, derive_more::Deref, derive_more::Display)]
2475 #[derive_deftly(ItemValueEncodable)]
2476 #[non_exhaustive]
2477 pub struct ContactInfo(#[deftly(netdoc(rest))] String);
2478
2479 #[derive(Clone, Debug, thiserror::Error)]
2481 #[error("contact information (`contact` item value) has invalid syntax")]
2482 #[non_exhaustive]
2483 pub struct InvalidContactInfo {}
2484
2485 impl FromStr for ContactInfo {
2486 type Err = InvalidContactInfo;
2487
2488 fn from_str(s: &str) -> Result<Self, InvalidContactInfo> {
2489 if s.contains('\n') || s.starts_with(char::is_whitespace) {
2493 Err(InvalidContactInfo {})
2494 } else {
2495 Ok(ContactInfo(s.to_owned()))
2496 }
2497 }
2498 }
2499
2500 impl ItemValueParseable for ContactInfo {
2501 fn from_unparsed(mut item: UnparsedItem<'_>) -> Result<Self, parse2::ErrorProblem> {
2502 item.check_no_object()?;
2503 item.args_mut()
2504 .into_remaining()
2505 .parse()
2506 .map_err(|_e| item.args().handle_error("info", ArgumentError::Invalid))
2507 }
2508 }
2509}
2510
2511mod boolean {
2513 use derive_deftly::Deftly;
2514 use std::{
2515 fmt::Display,
2516 ops::{Deref, DerefMut},
2517 str::FromStr,
2518 };
2519
2520 use crate::{Error, NetdocErrorKind as EK, NormalItemArgument, Pos};
2521
2522 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Deftly)]
2524 #[derive_deftly(Transparent)]
2525 #[allow(clippy::exhaustive_structs)]
2526 pub struct NumericBoolean(pub bool);
2527
2528 impl FromStr for NumericBoolean {
2529 type Err = Error;
2530
2531 fn from_str(s: &str) -> Result<Self, Self::Err> {
2532 match s {
2533 "0" => Ok(Self(false)),
2534 "1" => Ok(Self(true)),
2535 _ => Err(EK::BadArgument
2536 .at_pos(Pos::at(s))
2537 .with_msg("Invalid numeric boolean")),
2538 }
2539 }
2540 }
2541
2542 impl Display for NumericBoolean {
2543 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2544 write!(f, "{}", u8::from(self.0))
2545 }
2546 }
2547
2548 impl NormalItemArgument for NumericBoolean {}
2549}
2550
2551#[cfg(test)]
2552mod test {
2553 #![allow(clippy::bool_assert_comparison)]
2555 #![allow(clippy::clone_on_copy)]
2556 #![allow(clippy::dbg_macro)]
2557 #![allow(clippy::mixed_attributes_style)]
2558 #![allow(clippy::print_stderr)]
2559 #![allow(clippy::print_stdout)]
2560 #![allow(clippy::single_char_pattern)]
2561 #![allow(clippy::unwrap_used)]
2562 #![allow(clippy::unchecked_time_subtraction)]
2563 #![allow(clippy::useless_vec)]
2564 #![allow(clippy::needless_pass_by_value)]
2565 #![allow(clippy::string_slice)] use std::{
2568 fmt::Debug,
2569 time::{Duration, SystemTime},
2570 };
2571
2572 use itertools::Itertools;
2573
2574 use base64ct::Encoding;
2575 use tor_basic_utils::test_rng::testing_rng;
2576 use tor_cert::{CertType, CertifiedKey, Ed25519Cert, KeyUnknownCert};
2577 use tor_checkable::{TimeBound, timed::TimeRangeBound};
2578 use tor_llcrypto::pk::ed25519::{self, Ed25519Identity, Ed25519PublicKey, ExpandedKeypair};
2579
2580 use super::*;
2581 use crate::{
2582 Pos, Result,
2583 encode::{NetdocEncodable, encode_netdoc_unsigned},
2584 parse2::{ErrorProblem, ParseInput, VerifyFailed},
2585 types::EmbeddedCert,
2586 };
2587
2588 fn base64_decode_ignore_ws(s: &str) -> std::result::Result<Vec<u8>, base64ct::Error> {
2590 let mut s = s.to_string();
2591 s.retain(|c| !c.is_ascii_whitespace());
2592 base64ct::Base64::decode_vec(s.as_str())
2593 }
2594
2595 #[test]
2596 fn base64() -> Result<()> {
2597 assert_eq!("Mi43MTgyOA".parse::<B64>()?.as_bytes(), &b"2.71828"[..]);
2600 assert!("Mi43MTgyOA".parse::<B64>()?.check_len(7..8).is_ok());
2601 assert_eq!("Mg".parse::<B64>()?.as_bytes(), &b"2"[..]);
2602 assert!("Mg".parse::<B64>()?.check_len(1..2).is_ok());
2603 assert_eq!(
2604 "8J+NkvCfjZLwn42S8J+NkvCfjZLwn42S"
2605 .parse::<B64>()?
2606 .as_bytes(),
2607 "🍒🍒🍒🍒🍒🍒".as_bytes()
2608 );
2609 assert!(
2610 "8J+NkvCfjZLwn42S8J+NkvCfjZLwn42S"
2611 .parse::<B64>()?
2612 .check_len(24..25)
2613 .is_ok()
2614 );
2615 assert!(
2616 "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz8="
2617 .parse::<B64>()?
2618 .check_len(32..33)
2619 .is_ok()
2620 );
2621 assert_eq!("Mi43MTgyOA==".parse::<B64>()?.as_bytes(), &b"2.71828"[..]);
2623 assert!("Mi43MTgyOA==".parse::<B64>()?.check_len(7..8).is_ok());
2624 assert_eq!("Mg==".parse::<B64>()?.as_bytes(), &b"2"[..]);
2625 assert!("Mg==".parse::<B64>()?.check_len(1..2).is_ok());
2626
2627 assert!("Mi43!!!!!!".parse::<B64>().is_err());
2630 assert!("Mi".parse::<B64>().is_err());
2632 assert!(
2633 "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxaaaa"
2634 .parse::<B64>()
2635 .is_err()
2636 );
2637 assert!("Mi43MTgyOA".parse::<B64>()?.check_len(8..).is_err());
2639 Ok(())
2640 }
2641
2642 #[test]
2643 fn base64_lengths() -> Result<()> {
2644 assert_eq!("".parse::<B64>()?.as_bytes(), b"");
2645 assert!("=".parse::<B64>().is_err());
2646 assert!("==".parse::<B64>().is_err());
2647 assert!("B".parse::<B64>().is_err());
2648 assert!("B=".parse::<B64>().is_err());
2649 assert!("B==".parse::<B64>().is_err());
2650 assert!("Bg=".parse::<B64>().is_err());
2651 assert_eq!("Bg".parse::<B64>()?.as_bytes(), b"\x06");
2652 assert_eq!("Bg==".parse::<B64>()?.as_bytes(), b"\x06");
2653 assert_eq!("BCg".parse::<B64>()?.as_bytes(), b"\x04\x28");
2654 assert_eq!("BCg=".parse::<B64>()?.as_bytes(), b"\x04\x28");
2655 assert!("BCg==".parse::<B64>().is_err());
2656 assert_eq!("BCDE".parse::<B64>()?.as_bytes(), b"\x04\x20\xc4");
2657 assert!("BCDE=".parse::<B64>().is_err());
2658 assert!("BCDE==".parse::<B64>().is_err());
2659 Ok(())
2660 }
2661
2662 #[test]
2663 fn base64_rev() {
2664 use base64ct::{Base64, Base64Unpadded};
2665
2666 for n in 0..=5 {
2669 for c_vec in std::iter::repeat_n("ACEQg/=".chars(), n).multi_cartesian_product() {
2670 let s: String = c_vec.into_iter().collect();
2671 #[allow(clippy::print_stderr)]
2672 let b = match s.parse::<B64>() {
2673 Ok(b) => {
2674 eprintln!("{:10} {:?}", s, b.as_bytes());
2675 b
2676 }
2677 Err(_) => {
2678 eprintln!("{:10} Err", s);
2679 continue;
2680 }
2681 };
2682 let b = b.as_bytes();
2683
2684 let ep = Base64::encode_string(b);
2685 let eu = Base64Unpadded::encode_string(b);
2686
2687 assert!(
2688 s == ep || s == eu,
2689 "{:?} decoded to {:?} giving neither {:?} nor {:?}",
2690 s,
2691 b,
2692 ep,
2693 eu
2694 );
2695 }
2696 }
2697 }
2698
2699 #[test]
2700 fn base16() -> anyhow::Result<()> {
2701 let chk = |s: &str, b: &[u8]| -> anyhow::Result<()> {
2702 let parsed = s.parse::<B16>()?;
2703 assert_eq!(parsed.as_bytes(), b, "{s:?}");
2704 assert_eq!(parsed.to_string(), s.to_ascii_lowercase());
2705
2706 let parsed = s.parse::<B16U>()?;
2707 assert_eq!(parsed.as_bytes(), b, "{s:?}");
2708 assert_eq!(parsed.to_string(), s.to_ascii_uppercase());
2709 Ok(())
2710 };
2711
2712 chk("332e313432", b"3.142")?;
2713 chk("332E313432", b"3.142")?;
2714 chk("332E3134", b"3.14")?;
2715
2716 assert!("332E313".parse::<B16>().is_err());
2717 assert!("332G3134".parse::<B16>().is_err());
2718 Ok(())
2719 }
2720
2721 #[test]
2722 fn curve25519() -> Result<()> {
2723 use tor_llcrypto::pk::curve25519::PublicKey;
2724 let k1 = "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz8=";
2725 let k2 = hex::decode("a69c2d8475d6f245c3d1ff5f13b50f62c38002ee2e8f9391c12a2608cc4a933f")
2726 .unwrap();
2727 let k2: &[u8; 32] = &k2[..].try_into().unwrap();
2728
2729 let k1: PublicKey = k1.parse::<Curve25519Public>()?.into();
2730 assert_eq!(k1, (*k2).into());
2731
2732 assert!(
2733 "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz"
2734 .parse::<Curve25519Public>()
2735 .is_err()
2736 );
2737 assert!(
2738 "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORSomCMxKkz"
2739 .parse::<Curve25519Public>()
2740 .is_err()
2741 );
2742 assert!(
2743 "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5wSomCMxKkz"
2744 .parse::<Curve25519Public>()
2745 .is_err()
2746 );
2747 assert!(
2748 "ppwthHXW8kXD0f9fE7UPYsOAAu4ORwSomCMxKkz"
2749 .parse::<Curve25519Public>()
2750 .is_err()
2751 );
2752
2753 Ok(())
2754 }
2755
2756 #[test]
2757 fn ed25519() -> Result<()> {
2758 use tor_llcrypto::pk::ed25519::Ed25519Identity;
2759 let k1 = "WVIPQ8oArAqLY4XzkcpIOI6U8KsUJHBQhG8SC57qru0";
2760 let k2 = hex::decode("59520f43ca00ac0a8b6385f391ca48388e94f0ab14247050846f120b9eeaaeed")
2761 .unwrap();
2762
2763 let k1: Ed25519Identity = k1.parse::<Ed25519Public>()?.into();
2764 assert_eq!(k1, Ed25519Identity::from_bytes(&k2).unwrap());
2765
2766 assert!(
2767 "WVIPQ8oArAqLY4Xzk0!!!!8KsUJHBQhG8SC57qru"
2768 .parse::<Ed25519Public>()
2769 .is_err()
2770 );
2771 assert!(
2772 "WVIPQ8oArAqLY4XzkcpIU8KsUJHBQhG8SC57qru"
2773 .parse::<Ed25519Public>()
2774 .is_err()
2775 );
2776 assert!(
2777 "WVIPQ8oArAqLY4XzkcpIU8KsUJHBQhG8SC57qr"
2778 .parse::<Ed25519Public>()
2779 .is_err()
2780 );
2781 assert!(
2783 "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxaaaa"
2784 .parse::<Curve25519Public>()
2785 .is_err()
2786 );
2787 Ok(())
2788 }
2789
2790 #[test]
2791 fn time() -> Result<()> {
2792 use humantime::parse_rfc3339;
2793 use std::time::SystemTime;
2794
2795 let t = "2020-09-29 13:36:33".parse::<Iso8601TimeSp>()?;
2796 let t: SystemTime = t.into();
2797 assert_eq!(t, parse_rfc3339("2020-09-29T13:36:33Z").unwrap());
2798
2799 assert!("2020-FF-29 13:36:33".parse::<Iso8601TimeSp>().is_err());
2800 assert!("2020-09-29Q13:99:33".parse::<Iso8601TimeSp>().is_err());
2801 assert!("2020-09-29".parse::<Iso8601TimeSp>().is_err());
2802 assert!("too bad, waluigi time".parse::<Iso8601TimeSp>().is_err());
2803
2804 assert_eq!(
2805 "2020-09-29 13:36:33",
2806 "2020-09-29 13:36:33".parse::<Iso8601TimeSp>()?.to_string()
2807 );
2808
2809 let t = "2020-09-29T13:36:33".parse::<Iso8601TimeNoSp>()?;
2810 let t: SystemTime = t.into();
2811 assert_eq!(t, parse_rfc3339("2020-09-29T13:36:33Z").unwrap());
2812
2813 assert!("2020-09-29 13:36:33".parse::<Iso8601TimeNoSp>().is_err());
2814 assert!("2020-09-29Q13:99:33".parse::<Iso8601TimeNoSp>().is_err());
2815 assert!("2020-09-29".parse::<Iso8601TimeNoSp>().is_err());
2816 assert!("too bad, waluigi time".parse::<Iso8601TimeNoSp>().is_err());
2817
2818 assert_eq!(
2819 "2020-09-29T13:36:33",
2820 "2020-09-29T13:36:33"
2821 .parse::<Iso8601TimeNoSp>()?
2822 .to_string()
2823 );
2824
2825 Ok(())
2826 }
2827
2828 #[test]
2829 fn rsa_public_key() {
2830 let key_b64 = r#"
2832 MIIBigKCAYEAsDkzTcKS4kAF56R2ijb9qCek53tKC1EwMdpWMk58bB28fY6kHc55
2833 E7n1hB+LC5neZlx88GKuZ9k8P3g0MlO5ejalcfBdIIm28Nz86JXf/L23YnEpxnG/
2834 IpxZEcmx/EYN+vwp72W3DGuzyntaoaut6lGJk+O/aRCLLcTm4MNznvN1ackK2H6b
2835 Xm2ejRwtVRLoPKODJiPGl43snCfXXWsMH3IALFOgm0szPLv2fAJzBI8VWrUN81M/
2836 lgwJhG6+xbr1CkrXI5fKs/TNr0B0ydC9BIZplmPrnXaeNklnw1cqUJ1oxDSgBrvx
2837 rpDo7paObjSPV26opa68QKGa7Gu2MZQC3RzViNCbawka/108g6hSUkoM+Om2oivr
2838 DvtMOs10MjsfibEBVnwEhqnlb/gj3hJkYoGRsCwAyMIaMObHcmAevMJRWAjGCc8T
2839 GMS9dSmg1IZst+U+V2OCcIHXT6wZ1zPsBM0pYKVLCwtewaq1306k0n+ekriEo7eI
2840 FS3Dd/Dx/a6jAgMBAAE=
2841 "#;
2842 let key_bytes = base64_decode_ignore_ws(key_b64).unwrap();
2843 let rsa = RsaPublicParse1Helper::from_vec(key_bytes, Pos::None).unwrap();
2844
2845 let bits = tor_llcrypto::pk::rsa::PublicKey::from(rsa.clone()).bits();
2846 assert_eq!(bits, 3072);
2847
2848 assert!(rsa.clone().check_exponent(65537).is_ok());
2850 assert!(rsa.clone().check_exponent(1337).is_err());
2851 assert!(rsa.clone().check_len_eq(3072).is_ok());
2852 assert!(rsa.clone().check_len(1024..=4096).is_ok());
2853 assert!(rsa.clone().check_len(1024..=1024).is_err());
2854 assert!(rsa.check_len(4096..).is_err());
2855
2856 let failure = RsaPublicParse1Helper::from_vec(vec![1, 2, 3], Pos::None);
2858 assert!(failure.is_err());
2859 }
2860
2861 #[test]
2862 fn ed_cert() {
2863 use tor_llcrypto::pk::ed25519::Ed25519Identity;
2864
2865 let cert_b64 = r#"
2867 AQQABwRNAR6m3kq5h8i3wwac+Ti293opoOP8RKGP9MT0WD4Bbz7YAQAgBACGCdys
2868 G7AwsoYMIKenDN6In6ReiGF8jaYoGqmWKDVBdGGMDIZyNIq+VdhgtAB1EyNFHJU1
2869 jGM0ir9dackL+PIsHbzJH8s/P/8RfUsKIL6/ZHbn3nKMxLH/8kjtxp5ScAA=
2870 "#;
2871 let cert_bytes = base64_decode_ignore_ws(cert_b64).unwrap();
2872 let right_subject_key: Ed25519Identity = "HqbeSrmHyLfDBpz5OLb3eimg4/xEoY/0xPRYPgFvPtg"
2874 .parse::<Ed25519Public>()
2875 .unwrap()
2876 .into();
2877 let wrong_subject_key: Ed25519Identity = "WVIPQ8oArAqLY4XzkcpIOI6U8KsUJHBQhG8SC57qru0"
2879 .parse::<Ed25519Public>()
2880 .unwrap()
2881 .into();
2882
2883 let cert = UnvalidatedEdCert::from_vec(cert_bytes, Pos::None)
2885 .unwrap()
2886 .check_cert_type(tor_cert::CertType::IDENTITY_V_SIGNING)
2887 .unwrap()
2888 .check_subject_key_is(&right_subject_key)
2889 .unwrap();
2890 assert!(
2892 cert.clone()
2893 .check_cert_type(tor_cert::CertType::RSA_ID_X509)
2894 .is_err()
2895 );
2896 assert!(cert.check_subject_key_is(&wrong_subject_key).is_err());
2898
2899 let failure = UnvalidatedEdCert::from_vec(vec![1, 2, 3], Pos::None);
2901 assert!(failure.is_err());
2902 }
2903
2904 #[test]
2905 fn fingerprint() -> Result<()> {
2906 use tor_llcrypto::pk::rsa::RsaIdentity;
2907 let fp1 = "7467 A97D 19CD 2B4F 2BC0 388A A99C 5E67 710F 847E";
2908 let fp2 = "7467A97D19CD2B4F2BC0388AA99C5E67710F847E";
2909 let fp3 = "$7467A97D19CD2B4F2BC0388AA99C5E67710F847E";
2910 let fp4 = "$7467A97D19CD2B4F2BC0388AA99C5E67710F847E=fred";
2911
2912 let k = hex::decode(fp2).unwrap();
2913 let k = RsaIdentity::from_bytes(&k[..]).unwrap();
2914
2915 assert_eq!(RsaIdentity::from(fp1.parse::<SpFingerprint>()?), k);
2916 assert_eq!(RsaIdentity::from(fp2.parse::<SpFingerprint>()?), k);
2917 assert!(fp3.parse::<SpFingerprint>().is_err());
2918 assert!(fp4.parse::<SpFingerprint>().is_err());
2919
2920 assert!(fp1.parse::<Fingerprint>().is_err());
2921 assert_eq!(RsaIdentity::from(fp2.parse::<Fingerprint>()?), k);
2922 assert!(fp3.parse::<Fingerprint>().is_err());
2923 assert!(fp4.parse::<Fingerprint>().is_err());
2924 assert_eq!(Fingerprint(k).to_string(), fp2);
2925
2926 assert!(fp1.parse::<LongIdent>().is_err());
2927 assert_eq!(RsaIdentity::from(fp2.parse::<LongIdent>()?), k);
2928 assert_eq!(RsaIdentity::from(fp3.parse::<LongIdent>()?), k);
2929 assert_eq!(RsaIdentity::from(fp4.parse::<LongIdent>()?), k);
2930
2931 assert!("xxxx".parse::<Fingerprint>().is_err());
2932 assert!("ffffffffff".parse::<Fingerprint>().is_err());
2933
2934 let fp_b64 = "dGepfRnNK08rwDiKqZxeZ3EPhH4";
2935 assert_eq!(RsaIdentity::from(fp_b64.parse::<Base64Fingerprint>()?), k);
2936 assert_eq!(Base64Fingerprint(k).to_string(), fp_b64);
2937
2938 Ok(())
2939 }
2940
2941 #[test]
2942 fn nickname() -> anyhow::Result<()> {
2943 let n: Nickname = "Foo".parse()?;
2944 assert_eq!(n.as_str(), "Foo");
2945 assert_eq!(n.to_string(), "Foo");
2946
2947 let word = "Untr1gonometr1cally";
2948 assert_eq!(word.len(), 19);
2949 let long: Nickname = word.parse()?;
2950 assert_eq!(long.as_str(), word);
2951
2952 let too_long = "abcdefghijklmnopqrstuvwxyz";
2953 let not_ascii = "Eyjafjallajökull";
2954 let too_short = "";
2955 let other_invalid = "contains space";
2956 assert!(not_ascii.len() <= 19);
2957 assert!(too_long.parse::<Nickname>().is_err());
2958 assert!(not_ascii.parse::<Nickname>().is_err());
2959 assert!(too_short.parse::<Nickname>().is_err());
2960 assert!(other_invalid.parse::<Nickname>().is_err());
2961
2962 Ok(())
2963 }
2964
2965 #[test]
2967 fn hostname() {
2968 use std::net::IpAddr;
2969
2970 let chk_name = |s: &str| {
2972 let n: Hostname = s.parse().expect(s);
2973 assert_eq!(n.as_str(), s);
2974 assert_eq!(n.to_string(), s);
2975 assert_eq!(s.parse::<InternetHost>().expect(s), InternetHost::Name(n));
2976 };
2977
2978 let chk_either = |s: &str| {
2981 let h: InternetHost = s.parse().expect(s);
2982 let a: IpAddr = s.parse().expect(s);
2983 assert_eq!(h, InternetHost::IpAddr(a), "{s:?}");
2984 assert_eq!(h.to_string(), a.to_string(), "{s:?}");
2985 };
2986
2987 let chk_addr = |s: &str| {
2989 let _: InvalidHostname = s.parse::<Hostname>().expect_err(s);
2990 chk_either(s);
2991 };
2992
2993 let chk_bad = |s: &str| {
2995 let _: InvalidHostname = s.parse::<Hostname>().expect_err(s);
2996 let _: InvalidInternetHost = s.parse::<InternetHost>().expect_err(s);
2997 };
2998
2999 chk_name("foo.bar");
3000 chk_name("localhost");
3001 chk_name("tor.invalid");
3002 chk_name("example.com");
3003
3004 chk_bad("");
3006 chk_bad("foo bar");
3007 chk_bad("foo..bar");
3008 chk_bad("foo.-bar");
3009 chk_bad(" foo.bar ");
3010 chk_bad("[::1]");
3011
3012 chk_bad("1");
3018 chk_bad("127.0.0.023");
3019
3020 chk_bad("1.2.3.4.5");
3023
3024 chk_either("0.0.0.0");
3025 chk_either("127.0.0.1");
3026 chk_addr("::");
3027 chk_addr("::1");
3028 chk_addr("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
3029 chk_addr("::ffff:192.0.2.3"); }
3031
3032 #[test]
3033 fn contact_info() -> anyhow::Result<()> {
3034 use parse2::{ParseInput, parse_netdoc};
3035
3036 const S: &str = "some relay operator";
3037 let n: ContactInfo = S.parse()?;
3038 assert_eq!(n.as_str(), S);
3039 assert_eq!(n.to_string(), S);
3040
3041 let bad = |s: &str| {
3042 let _: InvalidContactInfo = s.parse::<ContactInfo>().unwrap_err();
3043 };
3044
3045 bad(" starts with space");
3046 bad("contains\nnewline");
3047
3048 #[derive(PartialEq, Debug, Deftly)]
3049 #[derive_deftly(NetdocParseable, NetdocEncodable)]
3050 struct TestDoc {
3051 pub intro: (),
3052 pub contact: ContactInfo,
3053 }
3054
3055 let roundtrip = |s: &str| -> anyhow::Result<()> {
3056 let doc = TestDoc {
3057 intro: (),
3058 contact: s.parse()?,
3059 };
3060 let enc = encode_netdoc_unsigned([&doc])?;
3061 let reparsed = parse_netdoc::<TestDoc>(&ParseInput::new(&enc, "<test>"))?;
3062 assert_eq!(doc, reparsed);
3063 Ok(())
3064 };
3065
3066 roundtrip("normal")?;
3067 roundtrip("trailing white space ")?;
3068 roundtrip("wtf is this allowed in \x03 netdocs\r")?; Ok(())
3071 }
3072
3073 #[test]
3076 fn numeric_boolean() {
3077 let chk = |s: &str| {
3078 assert_eq!(NumericBoolean::from_str(s).expect(s).to_string(), s);
3079 };
3080 chk("0");
3081 chk("1");
3082 assert!(NumericBoolean::from_str("10000").is_err());
3084 }
3085
3086 #[test]
3087 fn f64_finite() {
3088 let normalise_string = |i: &str, o: &str| {
3089 let v: F64Finite = i.parse().expect(i);
3090 assert_eq!(v.to_string(), o, "i={i:?}");
3091 };
3092 let roundtrip_string = |s: &str| normalise_string(s, s);
3093 let roundtrip_value = |i: f64| {
3094 let v: F64Finite = i.try_into().unwrap();
3095 let s = v.to_string();
3096 let o: F64Finite = s.parse().expect(&s);
3097 assert_eq!(v, o, "{i:?} {s}");
3098 assert_eq!(v.to_bits(), o.to_bits(), "{i:?} {s}");
3099 };
3100 let error_string = |s: &str| {
3101 let _: F64FiniteParseError = s.parse::<F64Finite>().expect_err(s);
3102 };
3103
3104 roundtrip_string("0");
3105 roundtrip_string("0.5");
3106 roundtrip_string("1");
3107 roundtrip_string("42");
3108 roundtrip_string("9007199254740991"); normalise_string("1e3", "1000");
3110
3111 roundtrip_value(f64::EPSILON);
3112 roundtrip_value(f64::EPSILON + 1.0);
3113 roundtrip_value(f64::MIN);
3114 roundtrip_value(f64::MIN_POSITIVE);
3115 roundtrip_value(-f64::MIN_POSITIVE);
3116 roundtrip_value(f64::MAX);
3117
3118 error_string(&f64::NAN.to_string());
3119 error_string(&f64::INFINITY.to_string());
3120 error_string("");
3121 error_string("garbage");
3122
3123 roundtrip_string(
3125 "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014",
3126 ); roundtrip_string(
3128 "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
3129 ); }
3131
3132 #[test]
3134 fn sp_fingerprint() {
3135 use derive_deftly::Deftly;
3136 use tor_llcrypto::pk::rsa::RsaIdentity;
3137
3138 use crate::parse2::ErrorProblem;
3139
3140 #[derive(Deftly)]
3141 #[derive_deftly(NetdocParseable, NetdocEncodable)]
3142 struct Wrapper {
3143 #[deftly(netdoc(single_arg))]
3144 fingerprint: SpFingerprint,
3145 }
3146
3147 fn parse2(s: &str) -> std::result::Result<SpFingerprint, ErrorProblem> {
3152 use crate::parse2::{self, ParseInput};
3153
3154 let input = format!("fingerprint {s}\n");
3155 let res = parse2::parse_netdoc::<Wrapper>(&ParseInput::new(&input, ""))
3156 .map_err(|x| x.problem)?;
3157
3158 let mut enc = NetdocEncoder::default();
3162 res.encode_unsigned(&mut enc).unwrap();
3163 assert!(input.starts_with(enc.finish().unwrap().trim_end()));
3164
3165 Ok(res.fingerprint)
3166 }
3167
3168 assert_eq!(
3170 parse2(&vec!["ABAB"; 10].join(" ")).unwrap(),
3171 SpFingerprint(RsaIdentity::from_bytes(&[0xAB; 20]).unwrap())
3172 );
3173
3174 assert_eq!(
3176 parse2(&vec!["ABAB"; 11].join(" ")).unwrap(),
3177 SpFingerprint(RsaIdentity::from_bytes(&[0xAB; 20]).unwrap())
3178 );
3179
3180 assert!(matches!(
3182 parse2(&vec!["ABAB"; 9].join(" ")).unwrap_err(),
3183 ErrorProblem::MissingArgument { .. }
3184 ));
3185
3186 assert!(matches!(
3191 parse2("0000 000000 00 0000 0000 0000 0000 0000 0000 0000").unwrap_err(),
3192 ErrorProblem::InvalidArgument { .. }
3193 ));
3194
3195 assert!(matches!(
3197 parse2(&vec!["ZZZZ"; 10].join(" ")).unwrap_err(),
3198 ErrorProblem::InvalidArgument { .. }
3199 ));
3200 }
3201
3202 #[test]
3204 fn item_present_parse2() {
3205 #[derive(Default)]
3206 struct Token;
3207
3208 #[derive(Deftly)]
3209 #[derive_deftly(NetdocParseable)]
3210 struct TestDoc {
3211 #[allow(unused)]
3212 intro: Ignored,
3213 foo: Option<ItemPresent<Token>>,
3214 }
3215
3216 let tests = [
3219 ("intro\nfoo\n", Ok(true)),
3221 ("intro\n", Ok(false)),
3223 ("intro\nfoo\nfoo\n", Err(ErrorProblem::ItemRepeated)),
3225 ("intro\nbar\nfoo\nfoo\n", Err(ErrorProblem::ItemRepeated)),
3227 ("intro\nfoo bar\n", Ok(true)),
3229 ("intro\nfoo bar baz\n", Ok(true)),
3231 (
3233 "intro\nfoo\n-----BEGIN RSA PUBLIC KEY-----\n-----END RSA PUBLIC KEY-----\n",
3234 Err(ErrorProblem::ObjectUnexpected),
3235 ),
3236 ];
3237
3238 for (input, expect) in tests {
3239 println!("{input:?}, {expect:?}");
3240
3241 let got = parse2::parse_netdoc::<TestDoc>(&ParseInput::new(input, ""))
3243 .map(|x| x.foo.is_some())
3244 .map_err(|e| e.problem);
3245 assert_eq!(got, expect);
3246 }
3247 }
3248
3249 #[test]
3250 fn item_present_encode() {
3251 #[derive(Default)]
3252 struct Token;
3253
3254 #[derive(Deftly)]
3255 #[derive_deftly(NetdocEncodable)]
3256 struct TestDoc {
3257 #[allow(unused)]
3258 intro: (),
3259 foo: Option<ItemPresent<Token>>,
3260 }
3261
3262 let tests = [
3263 (Some(ItemPresent(Token)), "intro\nfoo\n"),
3264 (None, "intro\n"),
3265 ];
3266
3267 for (present, output) in tests {
3268 let re_encoded = encode_netdoc_unsigned([&TestDoc {
3269 intro: (),
3270 foo: present,
3271 }])
3272 .unwrap();
3273 assert_eq!(re_encoded, output);
3274 }
3275 }
3276
3277 trait Ed25519CertTest: Sized + PartialEq + Eq + Debug {
3279 fn new(
3283 signing_key: ed25519::Ed25519Identity,
3284 certified_key: ed25519::Ed25519Identity,
3285 ) -> Self;
3286
3287 fn cert_type() -> CertType;
3289
3290 fn new_signed(
3295 signing_key: &ed25519::Keypair,
3296 certified_key: ed25519::Ed25519Identity,
3297 expiry: SystemTime,
3298 ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug>;
3299
3300 fn verify(
3305 signing_key: Option<ed25519::Ed25519Identity>,
3306 certified_key: ed25519::Ed25519Identity,
3307 cert: KeyUnknownCert,
3308 ) -> StdResult<TimeRangeBound<Self>, VerifyFailed>;
3309 }
3310
3311 impl Ed25519CertTest for Ed25519IdentityCert {
3312 fn new(
3313 signing_key: ed25519::Ed25519Identity,
3314 certified_key: ed25519::Ed25519Identity,
3315 ) -> Self {
3316 Self {
3317 id_ed25519: signing_key,
3318 sign_ed25519: certified_key,
3319 }
3320 }
3321
3322 fn cert_type() -> CertType {
3323 CertType::IDENTITY_V_SIGNING
3324 }
3325
3326 fn new_signed(
3327 signing_key: &ed25519::Keypair,
3328 certified_key: ed25519::Ed25519Identity,
3329 expiry: SystemTime,
3330 ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
3331 Self::new_signed(signing_key, certified_key, expiry)
3332 }
3333
3334 fn verify(
3335 _signing_key: Option<ed25519::Ed25519Identity>,
3336 _certified_key: ed25519::Ed25519Identity,
3337 cert: KeyUnknownCert,
3338 ) -> StdResult<TimeRangeBound<Self>, VerifyFailed> {
3339 Self::verify(cert)
3340 }
3341 }
3342
3343 impl Ed25519CertTest for Ed25519FamilyCert {
3344 fn new(
3345 signing_key: ed25519::Ed25519Identity,
3346 _certified_key: ed25519::Ed25519Identity,
3347 ) -> Self {
3348 Self {
3349 family_ed25519: signing_key,
3350 }
3351 }
3352
3353 fn cert_type() -> CertType {
3354 CertType::FAMILY_V_IDENTITY
3355 }
3356
3357 fn new_signed(
3358 signing_key: &ed25519::Keypair,
3359 certified_key: ed25519::Ed25519Identity,
3360 expiry: SystemTime,
3361 ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
3362 Self::new_signed(signing_key, certified_key, expiry)
3363 }
3364
3365 fn verify(
3366 _signing_key: Option<ed25519::Ed25519Identity>,
3367 certified_key: ed25519::Ed25519Identity,
3368 cert: KeyUnknownCert,
3369 ) -> StdResult<TimeRangeBound<Self>, VerifyFailed> {
3370 Self::verify(certified_key, cert)
3371 }
3372 }
3373
3374 impl Ed25519CertTest for Ed25519NtorCrossCert {
3375 fn new(
3376 _signing_key: ed25519::Ed25519Identity,
3377 _certified_key: ed25519::Ed25519Identity,
3378 ) -> Self {
3379 Self::dangerous_new_unverified()
3380 }
3381
3382 fn cert_type() -> CertType {
3383 CertType::NTOR_CC_IDENTITY
3384 }
3385
3386 fn new_signed(
3387 signing_key: &ed25519::Keypair,
3388 certified_key: ed25519::Ed25519Identity,
3389 expiry: SystemTime,
3390 ) -> StdResult<EmbeddedCert<Self, KeyUnknownCert>, Bug> {
3391 Self::new_signed(&ExpandedKeypair::from(signing_key), certified_key, expiry)
3392 }
3393
3394 fn verify(
3395 signing_key: Option<ed25519::Ed25519Identity>,
3396 certified_key: ed25519::Ed25519Identity,
3397 cert: KeyUnknownCert,
3398 ) -> StdResult<TimeRangeBound<Self>, VerifyFailed> {
3399 Self::verify(signing_key.unwrap(), certified_key, cert)
3400 }
3401 }
3402
3403 fn str_to_st(s: &str) -> SystemTime {
3405 Iso8601TimeSp::from_str(s).unwrap().0
3406 }
3407
3408 fn ed25519_cert_rng<T: Ed25519CertTest>() {
3410 let mut rng = testing_rng();
3411 let signing_key = ed25519::Keypair::generate(&mut rng);
3412 let certified_key = ed25519::Keypair::generate(&mut rng);
3413 let now = str_to_st("2000-01-01 06:00:00");
3414 let expiry = str_to_st("2000-01-01 12:00:00");
3415
3416 let embedded_cert =
3418 T::new_signed(&signing_key, certified_key.public_key().into(), expiry).unwrap();
3419 assert_eq!(
3420 *embedded_cert.get().unwrap(),
3421 T::new(
3422 signing_key.public_key().into(),
3423 certified_key.public_key().into()
3424 )
3425 );
3426
3427 let unverified = embedded_cert.raw_unverified().clone();
3429 assert_eq!(T::cert_type(), unverified.peek_cert_type());
3430 match unverified.peek_subject_key() {
3431 CertifiedKey::Ed25519(x) => assert_eq!(
3432 *x,
3433 ed25519::Ed25519Identity::from(certified_key.public_key())
3434 ),
3435 _ => panic!(),
3436 }
3437
3438 T::verify(
3440 Some(signing_key.public_key().into()),
3441 certified_key.public_key().into(),
3442 unverified.clone(),
3443 )
3444 .unwrap()
3445 .check_valid_at(&now)
3446 .unwrap();
3447
3448 T::verify(
3450 Some(signing_key.public_key().into()),
3451 certified_key.public_key().into(),
3452 unverified,
3453 )
3454 .unwrap()
3455 .extend_end_bound(Duration::from_secs(60 * 60))
3456 .check_valid_at(&now)
3457 .unwrap();
3458 }
3459
3460 fn ed25519_cert_invalid<T: Ed25519CertTest + 'static>(requires_signed_with_ext: bool) {
3462 let mut rng = testing_rng();
3463 let now = str_to_st("2000-01-01 06:00:00");
3464 let expiry = str_to_st("2000-01-01 12:00:00");
3465 let signing_key = ed25519::Keypair::generate(&mut rng);
3466 let signing_pk = ed25519::Ed25519Identity::from(signing_key.public_key());
3467 let certified_key = ed25519::Keypair::generate(&mut rng);
3468 let certified_pk = ed25519::Ed25519Identity::from(certified_key.public_key());
3469
3470 let mut tests: Vec<(_, _, CertifiedKey, _, _)> = vec![
3471 (
3476 T::cert_type(),
3477 now - Duration::from_secs(64 * 64 * 24),
3479 certified_pk.into(),
3480 Some(&signing_pk),
3481 &signing_key,
3482 ),
3483 (
3485 CertType::LINK_AUTH_X509,
3487 expiry,
3488 certified_pk.into(),
3489 Some(&signing_pk),
3490 &signing_key,
3491 ),
3492 (
3494 T::cert_type(),
3495 expiry,
3496 CertifiedKey::RsaSha256Digest(certified_pk.into()),
3498 Some(&signing_pk),
3499 &signing_key,
3500 ),
3501 ];
3506
3507 if requires_signed_with_ext {
3510 tests.push((
3511 T::cert_type(),
3512 expiry,
3513 certified_pk.into(),
3514 None,
3515 &signing_key,
3516 ));
3517 }
3518
3519 for (ctype, expiry, certified_key, signing_key, signing_kp) in tests {
3520 let mut builder = Ed25519Cert::builder()
3521 .cert_type(ctype)
3522 .expiration(expiry)
3523 .cert_key(certified_key.clone())
3524 .clone();
3525 if let Some(signing_key) = signing_key {
3526 builder = builder.signing_key(*signing_key).clone();
3527 }
3528 let cert = Ed25519Cert::decode(&builder.encode_and_sign(signing_kp).unwrap()).unwrap();
3529
3530 T::verify(
3534 signing_key.copied(),
3535 Ed25519Identity::from_bytes(certified_key.as_bytes()).unwrap(),
3536 cert,
3537 )
3538 .and_then(|expired| expired.check_valid_at(&now).map_err(|e| e.into()))
3539 .unwrap_err();
3540 }
3541 }
3542
3543 #[test]
3544 fn ed25519_cert_rng_test() {
3545 ed25519_cert_rng::<Ed25519IdentityCert>();
3546 ed25519_cert_rng::<Ed25519FamilyCert>();
3547 ed25519_cert_rng::<Ed25519NtorCrossCert>();
3548 }
3549
3550 #[test]
3551 fn ed25519_cert_invalid_test() {
3552 ed25519_cert_invalid::<Ed25519IdentityCert>(true);
3553 ed25519_cert_invalid::<Ed25519FamilyCert>(true);
3554 ed25519_cert_invalid::<Ed25519NtorCrossCert>(false);
3555 }
3556}