1pub(crate) use b16impl::*;
9pub use b64impl::*;
10pub(crate) use curve25519impl::*;
11pub(crate) use ed25519impl::*;
12#[cfg(any(feature = "routerdesc", feature = "hs-common"))]
13pub(crate) use edcert::*;
14pub(crate) use fingerprint::*;
15pub(crate) use rsa::*;
16pub use timeimpl::*;
17
18#[cfg(feature = "encode")]
19use {
20 crate::encode::{
21 self,
22 ItemEncoder,
23 ItemObjectEncodable,
24 ItemValueEncodable,
25 MultiplicitySelector as EMultiplicitySelector,
27 },
28 std::iter,
29};
30#[cfg(feature = "parse2")]
31use {
32 crate::parse2::multiplicity::{
33 ItemSetMethods,
34 MultiplicitySelector as P2MultiplicitySelector,
36 ObjectSetMethods,
37 },
38 crate::parse2::{ArgumentError, ArgumentStream, ItemArgumentParseable, ItemObjectParseable}, };
40
41pub use nickname::Nickname;
42
43pub use fingerprint::{Base64Fingerprint, Fingerprint};
44
45pub use identified_digest::{DigestName, IdentifiedDigest};
46
47pub use ignored_impl::{Ignored, IgnoredItemOrObjectValue, NotPresent};
48
49use crate::NormalItemArgument;
50use derive_deftly::{Deftly, define_derive_deftly};
51use std::cmp::{self, PartialOrd};
52use std::fmt::{self, Display};
53use std::marker::PhantomData;
54use std::str::FromStr;
55use tor_error::{Bug, ErrorReport as _, internal};
56use void::{ResultVoidExt as _, Void};
57
58pub(crate) trait FromBytes: Sized {
62 fn from_bytes(b: &[u8], p: crate::Pos) -> crate::Result<Self>;
64 fn from_vec(v: Vec<u8>, p: crate::Pos) -> crate::Result<Self> {
67 Self::from_bytes(&v[..], p)
68 }
69}
70
71mod b64impl {
73 use crate::{Error, NetdocErrorKind as EK, Pos, Result};
74 use base64ct::{Base64, Base64Unpadded, Encoding};
75 use std::fmt::{self, Display};
76 use std::ops::RangeBounds;
77 use subtle::{Choice, ConstantTimeEq};
78
79 #[derive(Clone)]
83 #[allow(clippy::derived_hash_with_manual_eq)]
84 #[derive(Hash, derive_more::Debug, derive_more::From, derive_more::Into)]
85 #[debug(r#"B64("{self}")"#)]
86 pub struct B64(Vec<u8>);
87
88 impl ConstantTimeEq for B64 {
89 fn ct_eq(&self, other: &B64) -> Choice {
90 self.0.ct_eq(&other.0)
91 }
92 }
93 impl PartialEq for B64 {
95 fn eq(&self, other: &B64) -> bool {
96 self.ct_eq(other).into()
97 }
98 }
99 impl Eq for B64 {}
100
101 impl std::str::FromStr for B64 {
102 type Err = Error;
103 fn from_str(s: &str) -> Result<Self> {
104 let v: core::result::Result<Vec<u8>, base64ct::Error> = match s.len() % 4 {
105 0 => Base64::decode_vec(s),
106 _ => Base64Unpadded::decode_vec(s),
107 };
108 let v = v.map_err(|_| {
109 EK::BadArgument
110 .with_msg("Invalid base64")
111 .at_pos(Pos::at(s))
112 })?;
113 Ok(B64(v))
114 }
115 }
116
117 impl Display for B64 {
118 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119 Display::fmt(&Base64Unpadded::encode_string(&self.0), f)
120 }
121 }
122
123 impl B64 {
124 pub fn as_bytes(&self) -> &[u8] {
126 &self.0[..]
127 }
128 pub(crate) fn check_len<B: RangeBounds<usize>>(self, bounds: B) -> Result<Self> {
131 if bounds.contains(&self.0.len()) {
132 Ok(self)
133 } else {
134 Err(EK::BadObjectVal.with_msg("Invalid length on base64 data"))
135 }
136 }
137
138 pub fn into_array<const N: usize>(self) -> Result<[u8; N]> {
142 self.0
143 .try_into()
144 .map_err(|_| EK::BadObjectVal.with_msg("Invalid length on base64 data"))
145 }
146 }
147}
148
149mod b16impl {
153 use crate::{Error, NetdocErrorKind as EK, Pos, Result};
154
155 pub(crate) struct B16(Vec<u8>);
157
158 impl std::str::FromStr for B16 {
159 type Err = Error;
160 fn from_str(s: &str) -> Result<Self> {
161 let bytes = hex::decode(s).map_err(|_| {
162 EK::BadArgument
163 .at_pos(Pos::at(s))
164 .with_msg("invalid hexadecimal")
165 })?;
166 Ok(B16(bytes))
167 }
168 }
169
170 impl B16 {
171 #[allow(unused)]
173 pub(crate) fn as_bytes(&self) -> &[u8] {
174 &self.0[..]
175 }
176 }
177
178 impl From<B16> for Vec<u8> {
179 fn from(w: B16) -> Vec<u8> {
180 w.0
181 }
182 }
183}
184
185mod curve25519impl {
189 use super::B64;
190 use crate::{Error, NetdocErrorKind as EK, Pos, Result};
191 use tor_llcrypto::pk::curve25519::PublicKey;
192
193 pub(crate) struct Curve25519Public(PublicKey);
195
196 impl std::str::FromStr for Curve25519Public {
197 type Err = Error;
198 fn from_str(s: &str) -> Result<Self> {
199 let b64: B64 = s.parse()?;
200 let array: [u8; 32] = b64.as_bytes().try_into().map_err(|_| {
201 EK::BadArgument
202 .at_pos(Pos::at(s))
203 .with_msg("bad length for curve25519 key.")
204 })?;
205 Ok(Curve25519Public(array.into()))
206 }
207 }
208
209 impl From<Curve25519Public> for PublicKey {
210 fn from(w: Curve25519Public) -> PublicKey {
211 w.0
212 }
213 }
214}
215
216mod ed25519impl {
220 use super::B64;
221 use crate::{Error, NetdocErrorKind as EK, Pos, Result};
222 use tor_llcrypto::pk::ed25519::Ed25519Identity;
223
224 pub(crate) struct Ed25519Public(Ed25519Identity);
227
228 impl std::str::FromStr for Ed25519Public {
229 type Err = Error;
230 fn from_str(s: &str) -> Result<Self> {
231 let b64: B64 = s.parse()?;
232 if b64.as_bytes().len() != 32 {
233 return Err(EK::BadArgument
234 .at_pos(Pos::at(s))
235 .with_msg("bad length for ed25519 key."));
236 }
237 let key = Ed25519Identity::from_bytes(b64.as_bytes()).ok_or_else(|| {
238 EK::BadArgument
239 .at_pos(Pos::at(s))
240 .with_msg("bad value for ed25519 key.")
241 })?;
242 Ok(Ed25519Public(key))
243 }
244 }
245
246 impl From<Ed25519Public> for Ed25519Identity {
247 fn from(pk: Ed25519Public) -> Ed25519Identity {
248 pk.0
249 }
250 }
251}
252
253mod ignored_impl {
257 use super::*;
258
259 #[cfg(feature = "parse2")]
260 use crate::parse2::ErrorProblem as EP;
261
262 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Default)]
289 #[allow(clippy::exhaustive_structs)]
290 #[cfg_attr(
291 feature = "parse2",
292 derive(Deftly),
293 derive_deftly(NetdocParseableFields)
294 )]
295 pub struct NotPresent;
296
297 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Default)]
312 #[cfg_attr(
313 feature = "parse2",
314 derive(Deftly),
315 derive_deftly(ItemValueParseable, NetdocParseableFields)
316 )]
317 #[allow(clippy::exhaustive_structs)]
318 pub struct Ignored;
319
320 pub struct IgnoredItemOrObjectValue(Void);
327
328 #[cfg(feature = "parse2")]
329 impl ItemSetMethods for P2MultiplicitySelector<NotPresent> {
330 type Each = Ignored;
331 type Field = NotPresent;
332 fn can_accumulate(self, _acc: &Option<NotPresent>) -> Result<(), EP> {
333 Ok(())
334 }
335 fn accumulate(self, _acc: &mut Option<NotPresent>, _item: Ignored) -> Result<(), EP> {
336 Ok(())
337 }
338 fn finish(self, _acc: Option<NotPresent>, _: &'static str) -> Result<NotPresent, EP> {
339 Ok(NotPresent)
340 }
341 }
342
343 #[cfg(feature = "parse2")]
344 impl ItemArgumentParseable for NotPresent {
345 fn from_args(_: &mut ArgumentStream) -> Result<NotPresent, ArgumentError> {
346 Ok(NotPresent)
347 }
348 }
349
350 #[cfg(feature = "parse2")]
351 impl ObjectSetMethods for P2MultiplicitySelector<NotPresent> {
352 type Field = NotPresent;
353 type Each = Void;
354 fn resolve_option(self, _found: Option<Void>) -> Result<NotPresent, EP> {
355 Ok(NotPresent)
356 }
357 }
358
359 #[cfg(feature = "encode")]
360 impl<'f> encode::MultiplicityMethods<'f> for EMultiplicitySelector<NotPresent> {
361 type Field = NotPresent;
362 type Each = Void;
363 fn iter_ordered(self, _: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
364 iter::empty()
365 }
366 }
367
368 #[cfg(feature = "encode")]
369 impl encode::OptionalityMethods for EMultiplicitySelector<NotPresent> {
370 type Field = NotPresent;
371 type Each = Void;
372 fn as_option<'f>(self, _: &'f Self::Field) -> Option<&'f Self::Each> {
373 None
374 }
375 }
376
377 impl FromStr for Ignored {
378 type Err = Void;
379 fn from_str(_s: &str) -> Result<Ignored, Void> {
380 Ok(Ignored)
381 }
382 }
383
384 #[cfg(feature = "parse2")]
385 impl ItemArgumentParseable for Ignored {
386 fn from_args(_: &mut ArgumentStream) -> Result<Ignored, ArgumentError> {
387 Ok(Ignored)
388 }
389 }
390
391 #[cfg(feature = "parse2")]
392 impl ItemObjectParseable for Ignored {
393 fn check_label(_label: &str) -> Result<(), EP> {
394 Ok(())
396 }
397 fn from_bytes(_input: &[u8]) -> Result<Self, EP> {
398 Ok(Ignored)
399 }
400 }
401
402 #[cfg(feature = "parse2")]
403 impl ObjectSetMethods for P2MultiplicitySelector<Ignored> {
404 type Field = Ignored;
405 type Each = Ignored;
406 fn resolve_option(self, _found: Option<Ignored>) -> Result<Ignored, EP> {
407 Ok(Ignored)
408 }
409 }
410
411 #[cfg(feature = "encode")]
412 impl<'f> encode::MultiplicityMethods<'f> for EMultiplicitySelector<Ignored> {
413 type Field = Ignored;
414 type Each = IgnoredItemOrObjectValue;
415 fn iter_ordered(self, _: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
416 iter::empty()
417 }
418 }
419
420 #[cfg(feature = "encode")]
421 impl encode::OptionalityMethods for EMultiplicitySelector<Ignored> {
422 type Field = Ignored;
423 type Each = IgnoredItemOrObjectValue;
424 fn as_option<'f>(self, _: &'f Self::Field) -> Option<&'f Self::Each> {
425 None
426 }
427 }
428
429 #[cfg(feature = "encode")]
430 impl ItemValueEncodable for IgnoredItemOrObjectValue {
431 fn write_item_value_onto(&self, _: ItemEncoder) -> Result<(), Bug> {
432 void::unreachable(self.0)
433 }
434 }
435
436 #[cfg(feature = "encode")]
437 impl ItemObjectEncodable for IgnoredItemOrObjectValue {
438 fn label(&self) -> &str {
439 void::unreachable(self.0)
440 }
441 fn write_object_onto(&self, _: &mut Vec<u8>) -> Result<(), Bug> {
442 void::unreachable(self.0)
443 }
444 }
445}
446
447#[derive(Debug, PartialEq, Clone, Copy, Hash)]
477#[non_exhaustive]
478pub enum Unknown<T> {
479 Discarded(PhantomData<T>),
481
482 #[cfg(feature = "retain-unknown")]
484 Retained(T),
485}
486
487impl<T> Unknown<T> {
488 pub fn new_discard() -> Self {
490 Unknown::Discarded(PhantomData)
491 }
492
493 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Unknown<U> {
495 self.try_map(move |t| Ok::<_, Void>(f(t))).void_unwrap()
496 }
497
498 pub fn try_map<U, E>(self, f: impl FnOnce(T) -> Result<U, E>) -> Result<Unknown<U>, E> {
500 Ok(match self {
501 Unknown::Discarded(_) => Unknown::Discarded(PhantomData),
502 #[cfg(feature = "retain-unknown")]
503 Unknown::Retained(t) => Unknown::Retained(f(t)?),
504 })
505 }
506
507 pub fn as_ref(&self) -> Option<&T> {
509 match self {
510 Unknown::Discarded(_) => None,
511 #[cfg(feature = "retain-unknown")]
512 Unknown::Retained(t) => Some(t),
513 }
514 }
515
516 #[cfg(feature = "retain-unknown")]
520 pub fn into_retained(self) -> Result<T, Bug> {
521 match self {
522 Unknown::Discarded(_) => Err(internal!("Unknown::retained but data not collected")),
523 Unknown::Retained(t) => Ok(t),
524 }
525 }
526
527 #[cfg(feature = "retain-unknown")]
529 pub fn new_retained_default() -> Self
530 where
531 T: Default,
532 {
533 Unknown::Retained(T::default())
534 }
535
536 pub fn with_mut_unknown(&mut self, f: impl FnOnce(&mut T)) {
544 match self {
545 Unknown::Discarded(_) => {}
546 #[cfg(feature = "retain-unknown")]
547 Unknown::Retained(t) => f(t),
548 }
549 }
550}
551
552impl<T: PartialOrd> PartialOrd for Unknown<T> {
553 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
554 use Unknown::*;
555 match (self, other) {
556 (Discarded(_), Discarded(_)) => Some(cmp::Ordering::Equal),
557 #[cfg(feature = "retain-unknown")]
558 (Discarded(_), Retained(_)) | (Retained(_), Discarded(_)) => None,
559 #[cfg(feature = "retain-unknown")]
560 (Retained(a), Retained(b)) => a.partial_cmp(b),
561 }
562 }
563}
564
565mod timeimpl {
569 use crate::{Error, NetdocErrorKind as EK, Pos, Result};
570 use std::time::SystemTime;
571 use time::{
572 OffsetDateTime, PrimitiveDateTime, format_description::FormatItem,
573 macros::format_description,
574 };
575
576 #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] #[derive(derive_more::Into, derive_more::From, derive_more::Deref)]
582 #[allow(clippy::exhaustive_structs)]
583 pub struct Iso8601TimeSp(pub SystemTime);
584
585 const ISO_8601SP_FMT: &[FormatItem] =
587 format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
588
589 impl std::str::FromStr for Iso8601TimeSp {
590 type Err = Error;
591 fn from_str(s: &str) -> Result<Iso8601TimeSp> {
592 let d = PrimitiveDateTime::parse(s, &ISO_8601SP_FMT).map_err(|e| {
593 EK::BadArgument
594 .at_pos(Pos::at(s))
595 .with_msg(format!("invalid time: {}", e))
596 })?;
597 Ok(Iso8601TimeSp(d.assume_utc().into()))
598 }
599 }
600
601 fn fmt_with(
606 t: SystemTime,
607 format_desc: &[FormatItem],
608 ) -> core::result::Result<String, std::fmt::Error> {
609 OffsetDateTime::from(t)
610 .format(format_desc)
611 .map_err(|_| std::fmt::Error)
612 }
613
614 impl std::fmt::Display for Iso8601TimeSp {
615 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
616 write!(f, "{}", fmt_with(self.0, ISO_8601SP_FMT)?)
617 }
618 }
619
620 #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] #[derive(derive_more::Into, derive_more::From, derive_more::Deref)]
631 #[allow(clippy::exhaustive_structs)]
632 pub struct Iso8601TimeNoSp(pub SystemTime);
633
634 const ISO_8601NOSP_FMT: &[FormatItem] =
636 format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]");
637
638 impl std::str::FromStr for Iso8601TimeNoSp {
639 type Err = Error;
640 fn from_str(s: &str) -> Result<Iso8601TimeNoSp> {
641 let d = PrimitiveDateTime::parse(s, &ISO_8601NOSP_FMT).map_err(|e| {
642 EK::BadArgument
643 .at_pos(Pos::at(s))
644 .with_msg(format!("invalid time: {}", e))
645 })?;
646 Ok(Iso8601TimeNoSp(d.assume_utc().into()))
647 }
648 }
649
650 impl std::fmt::Display for Iso8601TimeNoSp {
651 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
652 write!(f, "{}", fmt_with(self.0, ISO_8601NOSP_FMT)?)
653 }
654 }
655
656 impl crate::NormalItemArgument for Iso8601TimeNoSp {}
657}
658
659mod rsa {
661 use crate::{NetdocErrorKind as EK, Pos, Result};
662 use std::ops::RangeBounds;
663 use tor_llcrypto::pk::rsa::PublicKey;
664
665 pub(crate) const RSA_FIXED_EXPONENT: u32 = 65537;
669
670 pub(crate) const RSA_MIN_BITS: usize = 1024;
674
675 #[allow(non_camel_case_types)]
680 #[derive(Clone, Debug)]
681 pub(crate) struct RsaPublicParse1Helper(PublicKey, Pos);
682
683 impl From<RsaPublicParse1Helper> for PublicKey {
684 fn from(k: RsaPublicParse1Helper) -> PublicKey {
685 k.0
686 }
687 }
688 impl super::FromBytes for RsaPublicParse1Helper {
689 fn from_bytes(b: &[u8], pos: Pos) -> Result<Self> {
690 let key = PublicKey::from_der(b)
691 .ok_or_else(|| EK::BadObjectVal.with_msg("unable to decode RSA public key"))?;
692 Ok(RsaPublicParse1Helper(key, pos))
693 }
694 }
695 impl RsaPublicParse1Helper {
696 pub(crate) fn check_exponent(self, e: u32) -> Result<Self> {
698 if self.0.exponent_is(e) {
699 Ok(self)
700 } else {
701 Err(EK::BadObjectVal
702 .at_pos(self.1)
703 .with_msg("invalid RSA exponent"))
704 }
705 }
706 pub(crate) fn check_len<B: RangeBounds<usize>>(self, bounds: B) -> Result<Self> {
709 if bounds.contains(&self.0.bits()) {
710 Ok(self)
711 } else {
712 Err(EK::BadObjectVal
713 .at_pos(self.1)
714 .with_msg("invalid RSA length"))
715 }
716 }
717 pub(crate) fn check_len_eq(self, n: usize) -> Result<Self> {
720 self.check_len(n..=n)
721 }
722 }
723}
724
725#[cfg(any(feature = "routerdesc", feature = "hs-common"))]
727mod edcert {
728 use crate::{NetdocErrorKind as EK, Pos, Result};
729 use tor_cert::{CertType, Ed25519Cert, KeyUnknownCert};
730 #[cfg(feature = "routerdesc")]
731 use tor_llcrypto::pk::ed25519;
732
733 #[derive(Debug, Clone)]
736 pub(crate) struct UnvalidatedEdCert(KeyUnknownCert, Pos);
737
738 impl super::FromBytes for UnvalidatedEdCert {
739 fn from_bytes(b: &[u8], p: Pos) -> Result<Self> {
740 let cert = Ed25519Cert::decode(b).map_err(|e| {
741 EK::BadObjectVal
742 .at_pos(p)
743 .with_msg("Bad certificate")
744 .with_source(e)
745 })?;
746
747 Ok(Self(cert, p))
748 }
749 fn from_vec(v: Vec<u8>, p: Pos) -> Result<Self> {
750 Self::from_bytes(&v[..], p)
751 }
752 }
753 impl UnvalidatedEdCert {
754 pub(crate) fn check_cert_type(self, desired_type: CertType) -> Result<Self> {
756 if self.0.peek_cert_type() != desired_type {
757 return Err(EK::BadObjectVal.at_pos(self.1).with_msg(format!(
758 "bad certificate type {} (wanted {})",
759 self.0.peek_cert_type(),
760 desired_type
761 )));
762 }
763 Ok(self)
764 }
765 #[cfg(feature = "routerdesc")]
767 pub(crate) fn check_subject_key_is(self, pk: &ed25519::Ed25519Identity) -> Result<Self> {
768 if self.0.peek_subject_key().as_ed25519() != Some(pk) {
769 return Err(EK::BadObjectVal
770 .at_pos(self.1)
771 .with_msg("incorrect subject key"));
772 }
773 Ok(self)
774 }
775 pub(crate) fn into_unchecked(self) -> KeyUnknownCert {
777 self.0
778 }
779 }
780}
781
782mod identified_digest {
786 use super::*;
787
788 define_derive_deftly! {
789 StringReprUnitsOrUnknown for enum, expect items, beta_deftly:
804
805 ${define STRING_REPR {
806 ${vmeta(string_repr)
807 as str,
808 default { ${concat ${snake_case $vname}} }
809 }
810 }}
811
812 impl FromStr for $ttype {
813 type Err = Void;
814 fn from_str(s: &str) -> Result<Self, Void> {
815 $(
816 ${when v_is_unit}
817 if s == $STRING_REPR {
818 return Ok($vtype)
819 }
820 )
821 $(
822 ${when not(v_is_unit)} Ok($vtype { 0: s.into() })
825 )
826 }
827 }
828 impl Display for $ttype {
829 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
830 let s: &str = match self {
831 $(
832 ${when v_is_unit}
833 $vtype => $STRING_REPR,
834 )
835 $(
836 ${when not(v_is_unit)}
837 $vpat => f_0,
838 )
839 };
840 Display::fmt(s, f)
841 }
842 }
843 }
844
845 #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
849 #[derive_deftly(StringReprUnitsOrUnknown)]
850 #[non_exhaustive]
851 pub enum DigestName {
852 Sha256,
854 Unknown(String),
856 }
857
858 #[derive(Debug, Clone, Eq, PartialEq, Hash, derive_more::Display)]
860 #[display("{alg}={value}")]
861 #[non_exhaustive]
862 pub struct IdentifiedDigest {
863 alg: DigestName,
865
866 value: B64,
870 }
871
872 impl NormalItemArgument for DigestName {}
873 impl NormalItemArgument for IdentifiedDigest {}
874
875 #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, thiserror::Error)]
877 #[error("invalid syntax, expected ALGORITHM=DIGEST: {0}")]
878 pub struct IdentifiedDigestParseError(String);
879
880 impl FromStr for IdentifiedDigest {
881 type Err = IdentifiedDigestParseError;
882
883 fn from_str(s: &str) -> Result<Self, Self::Err> {
884 (|| {
885 let (alg, value) = s.split_once('=').ok_or("missing equals sign")?;
886
887 let alg = alg.parse().void_unwrap();
888 let value = value
889 .parse::<B64>()
890 .map_err(|e| format!("bad value: {}", e.report()))?;
891
892 if let Some(exp_len) = (|| {
893 Some({
894 use DigestName::*;
895 match alg {
896 Sha256 => 32,
897 Unknown(_) => None?,
898 }
899 })
900 })() {
901 let val_len = value.as_bytes().len();
902 if val_len != exp_len {
903 return Err(format!("got {val_len} bytes, expected {exp_len}"));
904 }
905 }
906
907 Ok(IdentifiedDigest { alg, value })
908 })()
909 .map_err(IdentifiedDigestParseError)
910 }
911 }
912}
913
914mod fingerprint {
916 use crate::{Error, NetdocErrorKind as EK, Pos, Result};
917 use base64ct::{Base64Unpadded, Encoding as _};
918 use std::fmt::{self, Display};
919 use tor_llcrypto::pk::rsa::RsaIdentity;
920
921 #[derive(Debug, Clone, Eq, PartialEq, derive_more::Deref, derive_more::Into)]
925 #[allow(clippy::exhaustive_structs)]
926 pub(crate) struct SpFingerprint(pub RsaIdentity);
927
928 #[derive(Debug, Clone, Eq, PartialEq, derive_more::Deref, derive_more::Into)]
932 #[allow(clippy::exhaustive_structs)]
933 pub struct Fingerprint(pub RsaIdentity);
934
935 #[derive(Debug, Clone, Eq, PartialEq, derive_more::Deref, derive_more::Into)]
939 #[allow(clippy::exhaustive_structs)]
940 pub struct Base64Fingerprint(pub RsaIdentity);
941
942 #[derive(Debug, Clone, Eq, PartialEq, derive_more::Deref, derive_more::Into)]
946 #[allow(clippy::exhaustive_structs)]
947 pub(crate) struct LongIdent(pub RsaIdentity);
948
949 fn parse_hex_ident(s: &str) -> Result<RsaIdentity> {
951 RsaIdentity::from_hex(s).ok_or_else(|| {
952 EK::BadArgument
953 .at_pos(Pos::at(s))
954 .with_msg("wrong length on fingerprint")
955 })
956 }
957
958 impl std::str::FromStr for SpFingerprint {
959 type Err = Error;
960 fn from_str(s: &str) -> Result<SpFingerprint> {
961 let ident = parse_hex_ident(&s.replace(' ', "")).map_err(|e| e.at_pos(Pos::at(s)))?;
962 Ok(SpFingerprint(ident))
963 }
964 }
965
966 impl std::str::FromStr for Base64Fingerprint {
967 type Err = Error;
968 fn from_str(s: &str) -> Result<Base64Fingerprint> {
969 let b = s.parse::<super::B64>()?;
970 let ident = RsaIdentity::from_bytes(b.as_bytes()).ok_or_else(|| {
971 EK::BadArgument
972 .at_pos(Pos::at(s))
973 .with_msg("Wrong identity length")
974 })?;
975 Ok(Base64Fingerprint(ident))
976 }
977 }
978
979 impl Display for Base64Fingerprint {
980 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
981 Display::fmt(&Base64Unpadded::encode_string(self.as_bytes()), f)
982 }
983 }
984
985 impl std::str::FromStr for Fingerprint {
986 type Err = Error;
987 fn from_str(s: &str) -> Result<Fingerprint> {
988 let ident = parse_hex_ident(s).map_err(|e| e.at_pos(Pos::at(s)))?;
989 Ok(Fingerprint(ident))
990 }
991 }
992
993 impl Display for Fingerprint {
994 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
995 Display::fmt(&hex::encode_upper(self.as_bytes()), f)
996 }
997 }
998
999 impl std::str::FromStr for LongIdent {
1000 type Err = Error;
1001 fn from_str(mut s: &str) -> Result<LongIdent> {
1002 if s.starts_with('$') {
1003 s = &s[1..];
1004 }
1005 if let Some(idx) = s.find(['=', '~']) {
1006 s = &s[..idx];
1007 }
1008 let ident = parse_hex_ident(s)?;
1009 Ok(LongIdent(ident))
1010 }
1011 }
1012
1013 impl crate::NormalItemArgument for Fingerprint {}
1014 impl crate::NormalItemArgument for Base64Fingerprint {}
1015}
1016
1017mod nickname {
1019 use crate::{Error, NetdocErrorKind as EK, Pos, Result};
1020 use tinystr::TinyAsciiStr;
1021
1022 const MAX_NICKNAME_LEN: usize = 19;
1024
1025 #[derive(Clone, Debug)]
1034 pub struct Nickname(tinystr::TinyAsciiStr<MAX_NICKNAME_LEN>);
1035
1036 impl Nickname {
1037 pub(crate) fn as_str(&self) -> &str {
1039 self.0.as_str()
1040 }
1041 }
1042
1043 impl std::fmt::Display for Nickname {
1044 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1045 self.as_str().fmt(f)
1046 }
1047 }
1048
1049 impl std::str::FromStr for Nickname {
1050 type Err = Error;
1051
1052 fn from_str(s: &str) -> Result<Self> {
1053 let tiny = TinyAsciiStr::from_str(s).map_err(|_| {
1054 EK::BadArgument
1055 .at_pos(Pos::at(s))
1056 .with_msg("Invalid nickname")
1057 })?;
1058
1059 if tiny.is_ascii_alphanumeric() && !tiny.is_empty() {
1060 Ok(Nickname(tiny))
1061 } else {
1062 Err(EK::BadArgument
1063 .at_pos(Pos::at(s))
1064 .with_msg("Invalid nickname"))
1065 }
1066 }
1067 }
1068
1069 impl crate::NormalItemArgument for Nickname {}
1070}
1071
1072#[cfg(test)]
1073mod test {
1074 #![allow(clippy::bool_assert_comparison)]
1076 #![allow(clippy::clone_on_copy)]
1077 #![allow(clippy::dbg_macro)]
1078 #![allow(clippy::mixed_attributes_style)]
1079 #![allow(clippy::print_stderr)]
1080 #![allow(clippy::print_stdout)]
1081 #![allow(clippy::single_char_pattern)]
1082 #![allow(clippy::unwrap_used)]
1083 #![allow(clippy::unchecked_time_subtraction)]
1084 #![allow(clippy::useless_vec)]
1085 #![allow(clippy::needless_pass_by_value)]
1086 use itertools::Itertools;
1088
1089 use base64ct::Encoding;
1090
1091 use super::*;
1092 use crate::{Pos, Result};
1093
1094 fn base64_decode_ignore_ws(s: &str) -> std::result::Result<Vec<u8>, base64ct::Error> {
1096 let mut s = s.to_string();
1097 s.retain(|c| !c.is_ascii_whitespace());
1098 base64ct::Base64::decode_vec(s.as_str())
1099 }
1100
1101 #[test]
1102 fn base64() -> Result<()> {
1103 assert_eq!("Mi43MTgyOA".parse::<B64>()?.as_bytes(), &b"2.71828"[..]);
1106 assert!("Mi43MTgyOA".parse::<B64>()?.check_len(7..8).is_ok());
1107 assert_eq!("Mg".parse::<B64>()?.as_bytes(), &b"2"[..]);
1108 assert!("Mg".parse::<B64>()?.check_len(1..2).is_ok());
1109 assert_eq!(
1110 "8J+NkvCfjZLwn42S8J+NkvCfjZLwn42S"
1111 .parse::<B64>()?
1112 .as_bytes(),
1113 "๐๐๐๐๐๐".as_bytes()
1114 );
1115 assert!(
1116 "8J+NkvCfjZLwn42S8J+NkvCfjZLwn42S"
1117 .parse::<B64>()?
1118 .check_len(24..25)
1119 .is_ok()
1120 );
1121 assert!(
1122 "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz8="
1123 .parse::<B64>()?
1124 .check_len(32..33)
1125 .is_ok()
1126 );
1127 assert_eq!("Mi43MTgyOA==".parse::<B64>()?.as_bytes(), &b"2.71828"[..]);
1129 assert!("Mi43MTgyOA==".parse::<B64>()?.check_len(7..8).is_ok());
1130 assert_eq!("Mg==".parse::<B64>()?.as_bytes(), &b"2"[..]);
1131 assert!("Mg==".parse::<B64>()?.check_len(1..2).is_ok());
1132
1133 assert!("Mi43!!!!!!".parse::<B64>().is_err());
1136 assert!("Mi".parse::<B64>().is_err());
1138 assert!(
1139 "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxaaaa"
1140 .parse::<B64>()
1141 .is_err()
1142 );
1143 assert!("Mi43MTgyOA".parse::<B64>()?.check_len(8..).is_err());
1145 Ok(())
1146 }
1147
1148 #[test]
1149 fn base64_lengths() -> Result<()> {
1150 assert_eq!("".parse::<B64>()?.as_bytes(), b"");
1151 assert!("=".parse::<B64>().is_err());
1152 assert!("==".parse::<B64>().is_err());
1153 assert!("B".parse::<B64>().is_err());
1154 assert!("B=".parse::<B64>().is_err());
1155 assert!("B==".parse::<B64>().is_err());
1156 assert!("Bg=".parse::<B64>().is_err());
1157 assert_eq!("Bg".parse::<B64>()?.as_bytes(), b"\x06");
1158 assert_eq!("Bg==".parse::<B64>()?.as_bytes(), b"\x06");
1159 assert_eq!("BCg".parse::<B64>()?.as_bytes(), b"\x04\x28");
1160 assert_eq!("BCg=".parse::<B64>()?.as_bytes(), b"\x04\x28");
1161 assert!("BCg==".parse::<B64>().is_err());
1162 assert_eq!("BCDE".parse::<B64>()?.as_bytes(), b"\x04\x20\xc4");
1163 assert!("BCDE=".parse::<B64>().is_err());
1164 assert!("BCDE==".parse::<B64>().is_err());
1165 Ok(())
1166 }
1167
1168 #[test]
1169 fn base64_rev() {
1170 use base64ct::{Base64, Base64Unpadded};
1171
1172 for n in 0..=5 {
1175 for c_vec in std::iter::repeat_n("ACEQg/=".chars(), n).multi_cartesian_product() {
1176 let s: String = c_vec.into_iter().collect();
1177 #[allow(clippy::print_stderr)]
1178 let b = match s.parse::<B64>() {
1179 Ok(b) => {
1180 eprintln!("{:10} {:?}", &s, b.as_bytes());
1181 b
1182 }
1183 Err(_) => {
1184 eprintln!("{:10} Err", &s);
1185 continue;
1186 }
1187 };
1188 let b = b.as_bytes();
1189
1190 let ep = Base64::encode_string(b);
1191 let eu = Base64Unpadded::encode_string(b);
1192
1193 assert!(
1194 s == ep || s == eu,
1195 "{:?} decoded to {:?} giving neither {:?} nor {:?}",
1196 s,
1197 b,
1198 ep,
1199 eu
1200 );
1201 }
1202 }
1203 }
1204
1205 #[test]
1206 fn base16() -> Result<()> {
1207 assert_eq!("332e313432".parse::<B16>()?.as_bytes(), &b"3.142"[..]);
1208 assert_eq!("332E313432".parse::<B16>()?.as_bytes(), &b"3.142"[..]);
1209 assert_eq!("332E3134".parse::<B16>()?.as_bytes(), &b"3.14"[..]);
1210 assert!("332E313".parse::<B16>().is_err());
1211 assert!("332G3134".parse::<B16>().is_err());
1212 Ok(())
1213 }
1214
1215 #[test]
1216 fn curve25519() -> Result<()> {
1217 use tor_llcrypto::pk::curve25519::PublicKey;
1218 let k1 = "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz8=";
1219 let k2 = hex::decode("a69c2d8475d6f245c3d1ff5f13b50f62c38002ee2e8f9391c12a2608cc4a933f")
1220 .unwrap();
1221 let k2: &[u8; 32] = &k2[..].try_into().unwrap();
1222
1223 let k1: PublicKey = k1.parse::<Curve25519Public>()?.into();
1224 assert_eq!(k1, (*k2).into());
1225
1226 assert!(
1227 "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz"
1228 .parse::<Curve25519Public>()
1229 .is_err()
1230 );
1231 assert!(
1232 "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORSomCMxKkz"
1233 .parse::<Curve25519Public>()
1234 .is_err()
1235 );
1236 assert!(
1237 "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5wSomCMxKkz"
1238 .parse::<Curve25519Public>()
1239 .is_err()
1240 );
1241 assert!(
1242 "ppwthHXW8kXD0f9fE7UPYsOAAu4ORwSomCMxKkz"
1243 .parse::<Curve25519Public>()
1244 .is_err()
1245 );
1246
1247 Ok(())
1248 }
1249
1250 #[test]
1251 fn ed25519() -> Result<()> {
1252 use tor_llcrypto::pk::ed25519::Ed25519Identity;
1253 let k1 = "WVIPQ8oArAqLY4XzkcpIOI6U8KsUJHBQhG8SC57qru0";
1254 let k2 = hex::decode("59520f43ca00ac0a8b6385f391ca48388e94f0ab14247050846f120b9eeaaeed")
1255 .unwrap();
1256
1257 let k1: Ed25519Identity = k1.parse::<Ed25519Public>()?.into();
1258 assert_eq!(k1, Ed25519Identity::from_bytes(&k2).unwrap());
1259
1260 assert!(
1261 "WVIPQ8oArAqLY4Xzk0!!!!8KsUJHBQhG8SC57qru"
1262 .parse::<Ed25519Public>()
1263 .is_err()
1264 );
1265 assert!(
1266 "WVIPQ8oArAqLY4XzkcpIU8KsUJHBQhG8SC57qru"
1267 .parse::<Ed25519Public>()
1268 .is_err()
1269 );
1270 assert!(
1271 "WVIPQ8oArAqLY4XzkcpIU8KsUJHBQhG8SC57qr"
1272 .parse::<Ed25519Public>()
1273 .is_err()
1274 );
1275 assert!(
1277 "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxaaaa"
1278 .parse::<Curve25519Public>()
1279 .is_err()
1280 );
1281 Ok(())
1282 }
1283
1284 #[test]
1285 fn time() -> Result<()> {
1286 use humantime::parse_rfc3339;
1287 use std::time::SystemTime;
1288
1289 let t = "2020-09-29 13:36:33".parse::<Iso8601TimeSp>()?;
1290 let t: SystemTime = t.into();
1291 assert_eq!(t, parse_rfc3339("2020-09-29T13:36:33Z").unwrap());
1292
1293 assert!("2020-FF-29 13:36:33".parse::<Iso8601TimeSp>().is_err());
1294 assert!("2020-09-29Q13:99:33".parse::<Iso8601TimeSp>().is_err());
1295 assert!("2020-09-29".parse::<Iso8601TimeSp>().is_err());
1296 assert!("too bad, waluigi time".parse::<Iso8601TimeSp>().is_err());
1297
1298 assert_eq!(
1299 "2020-09-29 13:36:33",
1300 "2020-09-29 13:36:33".parse::<Iso8601TimeSp>()?.to_string()
1301 );
1302
1303 let t = "2020-09-29T13:36:33".parse::<Iso8601TimeNoSp>()?;
1304 let t: SystemTime = t.into();
1305 assert_eq!(t, parse_rfc3339("2020-09-29T13:36:33Z").unwrap());
1306
1307 assert!("2020-09-29 13:36:33".parse::<Iso8601TimeNoSp>().is_err());
1308 assert!("2020-09-29Q13:99:33".parse::<Iso8601TimeNoSp>().is_err());
1309 assert!("2020-09-29".parse::<Iso8601TimeNoSp>().is_err());
1310 assert!("too bad, waluigi time".parse::<Iso8601TimeNoSp>().is_err());
1311
1312 assert_eq!(
1313 "2020-09-29T13:36:33",
1314 "2020-09-29T13:36:33"
1315 .parse::<Iso8601TimeNoSp>()?
1316 .to_string()
1317 );
1318
1319 Ok(())
1320 }
1321
1322 #[test]
1323 fn rsa_public_key() {
1324 let key_b64 = r#"
1326 MIIBigKCAYEAsDkzTcKS4kAF56R2ijb9qCek53tKC1EwMdpWMk58bB28fY6kHc55
1327 E7n1hB+LC5neZlx88GKuZ9k8P3g0MlO5ejalcfBdIIm28Nz86JXf/L23YnEpxnG/
1328 IpxZEcmx/EYN+vwp72W3DGuzyntaoaut6lGJk+O/aRCLLcTm4MNznvN1ackK2H6b
1329 Xm2ejRwtVRLoPKODJiPGl43snCfXXWsMH3IALFOgm0szPLv2fAJzBI8VWrUN81M/
1330 lgwJhG6+xbr1CkrXI5fKs/TNr0B0ydC9BIZplmPrnXaeNklnw1cqUJ1oxDSgBrvx
1331 rpDo7paObjSPV26opa68QKGa7Gu2MZQC3RzViNCbawka/108g6hSUkoM+Om2oivr
1332 DvtMOs10MjsfibEBVnwEhqnlb/gj3hJkYoGRsCwAyMIaMObHcmAevMJRWAjGCc8T
1333 GMS9dSmg1IZst+U+V2OCcIHXT6wZ1zPsBM0pYKVLCwtewaq1306k0n+ekriEo7eI
1334 FS3Dd/Dx/a6jAgMBAAE=
1335 "#;
1336 let key_bytes = base64_decode_ignore_ws(key_b64).unwrap();
1337 let rsa = RsaPublicParse1Helper::from_vec(key_bytes, Pos::None).unwrap();
1338
1339 let bits = tor_llcrypto::pk::rsa::PublicKey::from(rsa.clone()).bits();
1340 assert_eq!(bits, 3072);
1341
1342 assert!(rsa.clone().check_exponent(65537).is_ok());
1344 assert!(rsa.clone().check_exponent(1337).is_err());
1345 assert!(rsa.clone().check_len_eq(3072).is_ok());
1346 assert!(rsa.clone().check_len(1024..=4096).is_ok());
1347 assert!(rsa.clone().check_len(1024..=1024).is_err());
1348 assert!(rsa.check_len(4096..).is_err());
1349
1350 let failure = RsaPublicParse1Helper::from_vec(vec![1, 2, 3], Pos::None);
1352 assert!(failure.is_err());
1353 }
1354
1355 #[cfg(feature = "routerdesc")]
1356 #[test]
1357 fn ed_cert() {
1358 use tor_llcrypto::pk::ed25519::Ed25519Identity;
1359
1360 let cert_b64 = r#"
1362 AQQABwRNAR6m3kq5h8i3wwac+Ti293opoOP8RKGP9MT0WD4Bbz7YAQAgBACGCdys
1363 G7AwsoYMIKenDN6In6ReiGF8jaYoGqmWKDVBdGGMDIZyNIq+VdhgtAB1EyNFHJU1
1364 jGM0ir9dackL+PIsHbzJH8s/P/8RfUsKIL6/ZHbn3nKMxLH/8kjtxp5ScAA=
1365 "#;
1366 let cert_bytes = base64_decode_ignore_ws(cert_b64).unwrap();
1367 let right_subject_key: Ed25519Identity = "HqbeSrmHyLfDBpz5OLb3eimg4/xEoY/0xPRYPgFvPtg"
1369 .parse::<Ed25519Public>()
1370 .unwrap()
1371 .into();
1372 let wrong_subject_key: Ed25519Identity = "WVIPQ8oArAqLY4XzkcpIOI6U8KsUJHBQhG8SC57qru0"
1374 .parse::<Ed25519Public>()
1375 .unwrap()
1376 .into();
1377
1378 let cert = UnvalidatedEdCert::from_vec(cert_bytes, Pos::None)
1380 .unwrap()
1381 .check_cert_type(tor_cert::CertType::IDENTITY_V_SIGNING)
1382 .unwrap()
1383 .check_subject_key_is(&right_subject_key)
1384 .unwrap();
1385 assert!(
1387 cert.clone()
1388 .check_cert_type(tor_cert::CertType::RSA_ID_X509)
1389 .is_err()
1390 );
1391 assert!(cert.check_subject_key_is(&wrong_subject_key).is_err());
1393
1394 let failure = UnvalidatedEdCert::from_vec(vec![1, 2, 3], Pos::None);
1396 assert!(failure.is_err());
1397 }
1398
1399 #[test]
1400 fn fingerprint() -> Result<()> {
1401 use tor_llcrypto::pk::rsa::RsaIdentity;
1402 let fp1 = "7467 A97D 19CD 2B4F 2BC0 388A A99C 5E67 710F 847E";
1403 let fp2 = "7467A97D19CD2B4F2BC0388AA99C5E67710F847E";
1404 let fp3 = "$7467A97D19CD2B4F2BC0388AA99C5E67710F847E";
1405 let fp4 = "$7467A97D19CD2B4F2BC0388AA99C5E67710F847E=fred";
1406
1407 let k = hex::decode(fp2).unwrap();
1408 let k = RsaIdentity::from_bytes(&k[..]).unwrap();
1409
1410 assert_eq!(RsaIdentity::from(fp1.parse::<SpFingerprint>()?), k);
1411 assert_eq!(RsaIdentity::from(fp2.parse::<SpFingerprint>()?), k);
1412 assert!(fp3.parse::<SpFingerprint>().is_err());
1413 assert!(fp4.parse::<SpFingerprint>().is_err());
1414
1415 assert!(fp1.parse::<Fingerprint>().is_err());
1416 assert_eq!(RsaIdentity::from(fp2.parse::<Fingerprint>()?), k);
1417 assert!(fp3.parse::<Fingerprint>().is_err());
1418 assert!(fp4.parse::<Fingerprint>().is_err());
1419 assert_eq!(Fingerprint(k).to_string(), fp2);
1420
1421 assert!(fp1.parse::<LongIdent>().is_err());
1422 assert_eq!(RsaIdentity::from(fp2.parse::<LongIdent>()?), k);
1423 assert_eq!(RsaIdentity::from(fp3.parse::<LongIdent>()?), k);
1424 assert_eq!(RsaIdentity::from(fp4.parse::<LongIdent>()?), k);
1425
1426 assert!("xxxx".parse::<Fingerprint>().is_err());
1427 assert!("ffffffffff".parse::<Fingerprint>().is_err());
1428
1429 let fp_b64 = "dGepfRnNK08rwDiKqZxeZ3EPhH4";
1430 assert_eq!(RsaIdentity::from(fp_b64.parse::<Base64Fingerprint>()?), k);
1431 assert_eq!(Base64Fingerprint(k).to_string(), fp_b64);
1432
1433 Ok(())
1434 }
1435
1436 #[test]
1437 fn nickname() -> Result<()> {
1438 let n: Nickname = "Foo".parse()?;
1439 assert_eq!(n.as_str(), "Foo");
1440 assert_eq!(n.to_string(), "Foo");
1441
1442 let word = "Untr1gonometr1cally";
1443 assert_eq!(word.len(), 19);
1444 let long: Nickname = word.parse()?;
1445 assert_eq!(long.as_str(), word);
1446
1447 let too_long = "abcdefghijklmnopqrstuvwxyz";
1448 let not_ascii = "Eyjafjallajรถkull";
1449 let too_short = "";
1450 let other_invalid = "contains space";
1451 assert!(not_ascii.len() <= 19);
1452 assert!(too_long.parse::<Nickname>().is_err());
1453 assert!(not_ascii.parse::<Nickname>().is_err());
1454 assert!(too_short.parse::<Nickname>().is_err());
1455 assert!(other_invalid.parse::<Nickname>().is_err());
1456
1457 Ok(())
1458 }
1459}