1pub(crate) use b16impl::*;
9pub(crate) 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(crate) use timeimpl::*;
17
18#[cfg(feature = "dangerous-expose-struct-fields")]
19pub use nickname::Nickname;
20#[cfg(not(feature = "dangerous-expose-struct-fields"))]
21pub(crate) use nickname::Nickname;
22
23pub(crate) trait FromBytes: Sized {
27 fn from_bytes(b: &[u8], p: crate::Pos) -> crate::Result<Self>;
29 fn from_vec(v: Vec<u8>, p: crate::Pos) -> crate::Result<Self> {
32 Self::from_bytes(&v[..], p)
33 }
34}
35
36mod b64impl {
38 use crate::{Error, NetdocErrorKind as EK, Pos, Result};
39 use base64ct::{Base64, Base64Unpadded, Encoding};
40 use std::ops::RangeBounds;
41
42 pub(crate) struct B64(Vec<u8>);
44
45 impl std::str::FromStr for B64 {
46 type Err = Error;
47 fn from_str(s: &str) -> Result<Self> {
48 let v: core::result::Result<Vec<u8>, base64ct::Error> = match s.len() % 4 {
49 0 => Base64::decode_vec(s),
50 _ => Base64Unpadded::decode_vec(s),
51 };
52 let v = v.map_err(|_| {
53 EK::BadArgument
54 .with_msg("Invalid base64")
55 .at_pos(Pos::at(s))
56 })?;
57 Ok(B64(v))
58 }
59 }
60
61 impl B64 {
62 pub(crate) fn as_bytes(&self) -> &[u8] {
64 &self.0[..]
65 }
66 pub(crate) fn check_len<B: RangeBounds<usize>>(self, bounds: B) -> Result<Self> {
69 if bounds.contains(&self.0.len()) {
70 Ok(self)
71 } else {
72 Err(EK::BadObjectVal.with_msg("Invalid length on base64 data"))
73 }
74 }
75
76 pub(crate) fn into_array<const N: usize>(self) -> Result<[u8; N]> {
80 self.0
81 .try_into()
82 .map_err(|_| EK::BadObjectVal.with_msg("Invalid length on base64 data"))
83 }
84 }
85
86 impl From<B64> for Vec<u8> {
87 fn from(w: B64) -> Vec<u8> {
88 w.0
89 }
90 }
91}
92
93mod b16impl {
97 use crate::{Error, NetdocErrorKind as EK, Pos, Result};
98
99 pub(crate) struct B16(Vec<u8>);
101
102 impl std::str::FromStr for B16 {
103 type Err = Error;
104 fn from_str(s: &str) -> Result<Self> {
105 let bytes = hex::decode(s).map_err(|_| {
106 EK::BadArgument
107 .at_pos(Pos::at(s))
108 .with_msg("invalid hexadecimal")
109 })?;
110 Ok(B16(bytes))
111 }
112 }
113
114 impl B16 {
115 #[allow(unused)]
117 pub(crate) fn as_bytes(&self) -> &[u8] {
118 &self.0[..]
119 }
120 }
121
122 impl From<B16> for Vec<u8> {
123 fn from(w: B16) -> Vec<u8> {
124 w.0
125 }
126 }
127}
128
129mod curve25519impl {
133 use super::B64;
134 use crate::{Error, NetdocErrorKind as EK, Pos, Result};
135 use tor_llcrypto::pk::curve25519::PublicKey;
136
137 pub(crate) struct Curve25519Public(PublicKey);
139
140 impl std::str::FromStr for Curve25519Public {
141 type Err = Error;
142 fn from_str(s: &str) -> Result<Self> {
143 let b64: B64 = s.parse()?;
144 let array: [u8; 32] = b64.as_bytes().try_into().map_err(|_| {
145 EK::BadArgument
146 .at_pos(Pos::at(s))
147 .with_msg("bad length for curve25519 key.")
148 })?;
149 Ok(Curve25519Public(array.into()))
150 }
151 }
152
153 impl From<Curve25519Public> for PublicKey {
154 fn from(w: Curve25519Public) -> PublicKey {
155 w.0
156 }
157 }
158}
159
160mod ed25519impl {
164 use super::B64;
165 use crate::{Error, NetdocErrorKind as EK, Pos, Result};
166 use tor_llcrypto::pk::ed25519::Ed25519Identity;
167
168 pub(crate) struct Ed25519Public(Ed25519Identity);
171
172 impl std::str::FromStr for Ed25519Public {
173 type Err = Error;
174 fn from_str(s: &str) -> Result<Self> {
175 let b64: B64 = s.parse()?;
176 if b64.as_bytes().len() != 32 {
177 return Err(EK::BadArgument
178 .at_pos(Pos::at(s))
179 .with_msg("bad length for ed25519 key."));
180 }
181 let key = Ed25519Identity::from_bytes(b64.as_bytes()).ok_or_else(|| {
182 EK::BadArgument
183 .at_pos(Pos::at(s))
184 .with_msg("bad value for ed25519 key.")
185 })?;
186 Ok(Ed25519Public(key))
187 }
188 }
189
190 impl From<Ed25519Public> for Ed25519Identity {
191 fn from(pk: Ed25519Public) -> Ed25519Identity {
192 pk.0
193 }
194 }
195}
196
197mod timeimpl {
201 use crate::{Error, NetdocErrorKind as EK, Pos, Result};
202 use std::time::SystemTime;
203 use time::{
204 format_description::FormatItem, macros::format_description, OffsetDateTime,
205 PrimitiveDateTime,
206 };
207
208 #[derive(derive_more::Into, derive_more::From)]
213 pub(crate) struct Iso8601TimeSp(SystemTime);
214
215 const ISO_8601SP_FMT: &[FormatItem] =
217 format_description!("[year]-[month]-[day] [hour]:[minute]:[second]");
218
219 impl std::str::FromStr for Iso8601TimeSp {
220 type Err = Error;
221 fn from_str(s: &str) -> Result<Iso8601TimeSp> {
222 let d = PrimitiveDateTime::parse(s, &ISO_8601SP_FMT).map_err(|e| {
223 EK::BadArgument
224 .at_pos(Pos::at(s))
225 .with_msg(format!("invalid time: {}", e))
226 })?;
227 Ok(Iso8601TimeSp(d.assume_utc().into()))
228 }
229 }
230
231 fn fmt_with(
236 t: SystemTime,
237 format_desc: &[FormatItem],
238 ) -> core::result::Result<String, std::fmt::Error> {
239 OffsetDateTime::from(t)
240 .format(format_desc)
241 .map_err(|_| std::fmt::Error)
242 }
243
244 impl std::fmt::Display for Iso8601TimeSp {
245 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
246 write!(f, "{}", fmt_with(self.0, ISO_8601SP_FMT)?)
247 }
248 }
249
250 #[derive(derive_more::Into, derive_more::From)]
260 pub(crate) struct Iso8601TimeNoSp(SystemTime);
261
262 const ISO_8601NOSP_FMT: &[FormatItem] =
264 format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]");
265
266 impl std::str::FromStr for Iso8601TimeNoSp {
267 type Err = Error;
268 fn from_str(s: &str) -> Result<Iso8601TimeNoSp> {
269 let d = PrimitiveDateTime::parse(s, &ISO_8601NOSP_FMT).map_err(|e| {
270 EK::BadArgument
271 .at_pos(Pos::at(s))
272 .with_msg(format!("invalid time: {}", e))
273 })?;
274 Ok(Iso8601TimeNoSp(d.assume_utc().into()))
275 }
276 }
277
278 impl std::fmt::Display for Iso8601TimeNoSp {
279 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
280 write!(f, "{}", fmt_with(self.0, ISO_8601NOSP_FMT)?)
281 }
282 }
283}
284
285mod rsa {
287 use crate::{NetdocErrorKind as EK, Pos, Result};
288 use std::ops::RangeBounds;
289 use tor_llcrypto::pk::rsa::PublicKey;
290
291 #[allow(non_camel_case_types)]
293 #[derive(Clone, Debug)]
294 pub(crate) struct RsaPublic(PublicKey, Pos);
295
296 impl From<RsaPublic> for PublicKey {
297 fn from(k: RsaPublic) -> PublicKey {
298 k.0
299 }
300 }
301 impl super::FromBytes for RsaPublic {
302 fn from_bytes(b: &[u8], pos: Pos) -> Result<Self> {
303 let key = PublicKey::from_der(b)
304 .ok_or_else(|| EK::BadObjectVal.with_msg("unable to decode RSA public key"))?;
305 Ok(RsaPublic(key, pos))
306 }
307 }
308 impl RsaPublic {
309 pub(crate) fn check_exponent(self, e: u32) -> Result<Self> {
311 if self.0.exponent_is(e) {
312 Ok(self)
313 } else {
314 Err(EK::BadObjectVal
315 .at_pos(self.1)
316 .with_msg("invalid RSA exponent"))
317 }
318 }
319 pub(crate) fn check_len<B: RangeBounds<usize>>(self, bounds: B) -> Result<Self> {
322 if bounds.contains(&self.0.bits()) {
323 Ok(self)
324 } else {
325 Err(EK::BadObjectVal
326 .at_pos(self.1)
327 .with_msg("invalid RSA length"))
328 }
329 }
330 pub(crate) fn check_len_eq(self, n: usize) -> Result<Self> {
333 self.check_len(n..=n)
334 }
335 }
336}
337
338#[cfg(any(feature = "routerdesc", feature = "hs-common"))]
340mod edcert {
341 use crate::{NetdocErrorKind as EK, Pos, Result};
342 use tor_cert::{CertType, Ed25519Cert, KeyUnknownCert};
343 #[cfg(feature = "routerdesc")]
344 use tor_llcrypto::pk::ed25519;
345
346 #[derive(Debug, Clone)]
349 pub(crate) struct UnvalidatedEdCert(KeyUnknownCert, Pos);
350
351 impl super::FromBytes for UnvalidatedEdCert {
352 fn from_bytes(b: &[u8], p: Pos) -> Result<Self> {
353 let cert = Ed25519Cert::decode(b).map_err(|e| {
354 EK::BadObjectVal
355 .at_pos(p)
356 .with_msg("Bad certificate")
357 .with_source(e)
358 })?;
359
360 Ok(Self(cert, p))
361 }
362 fn from_vec(v: Vec<u8>, p: Pos) -> Result<Self> {
363 Self::from_bytes(&v[..], p)
364 }
365 }
366 impl UnvalidatedEdCert {
367 pub(crate) fn check_cert_type(self, desired_type: CertType) -> Result<Self> {
369 if self.0.peek_cert_type() != desired_type {
370 return Err(EK::BadObjectVal.at_pos(self.1).with_msg(format!(
371 "bad certificate type {} (wanted {})",
372 self.0.peek_cert_type(),
373 desired_type
374 )));
375 }
376 Ok(self)
377 }
378 #[cfg(feature = "routerdesc")]
380 pub(crate) fn check_subject_key_is(self, pk: &ed25519::Ed25519Identity) -> Result<Self> {
381 if self.0.peek_subject_key().as_ed25519() != Some(pk) {
382 return Err(EK::BadObjectVal
383 .at_pos(self.1)
384 .with_msg("incorrect subject key"));
385 }
386 Ok(self)
387 }
388 pub(crate) fn into_unchecked(self) -> KeyUnknownCert {
390 self.0
391 }
392 }
393}
394
395mod fingerprint {
397 use crate::{Error, NetdocErrorKind as EK, Pos, Result};
398 use tor_llcrypto::pk::rsa::RsaIdentity;
399
400 pub(crate) struct SpFingerprint(RsaIdentity);
402
403 pub(crate) struct Fingerprint(RsaIdentity);
405
406 pub(crate) struct LongIdent(RsaIdentity);
408
409 impl From<SpFingerprint> for RsaIdentity {
410 fn from(f: SpFingerprint) -> RsaIdentity {
411 f.0
412 }
413 }
414
415 impl From<LongIdent> for RsaIdentity {
416 fn from(f: LongIdent) -> RsaIdentity {
417 f.0
418 }
419 }
420
421 impl From<Fingerprint> for RsaIdentity {
422 fn from(f: Fingerprint) -> RsaIdentity {
423 f.0
424 }
425 }
426
427 fn parse_hex_ident(s: &str) -> Result<RsaIdentity> {
429 RsaIdentity::from_hex(s).ok_or_else(|| {
430 EK::BadArgument
431 .at_pos(Pos::at(s))
432 .with_msg("wrong length on fingerprint")
433 })
434 }
435
436 impl std::str::FromStr for SpFingerprint {
437 type Err = Error;
438 fn from_str(s: &str) -> Result<SpFingerprint> {
439 let ident = parse_hex_ident(&s.replace(' ', "")).map_err(|e| e.at_pos(Pos::at(s)))?;
440 Ok(SpFingerprint(ident))
441 }
442 }
443
444 impl std::str::FromStr for Fingerprint {
445 type Err = Error;
446 fn from_str(s: &str) -> Result<Fingerprint> {
447 let ident = parse_hex_ident(s).map_err(|e| e.at_pos(Pos::at(s)))?;
448 Ok(Fingerprint(ident))
449 }
450 }
451
452 impl std::str::FromStr for LongIdent {
453 type Err = Error;
454 fn from_str(mut s: &str) -> Result<LongIdent> {
455 if s.starts_with('$') {
456 s = &s[1..];
457 }
458 if let Some(idx) = s.find(['=', '~']) {
459 s = &s[..idx];
460 }
461 let ident = parse_hex_ident(s)?;
462 Ok(LongIdent(ident))
463 }
464 }
465}
466
467mod nickname {
469 use crate::{Error, NetdocErrorKind as EK, Pos, Result};
470 use tinystr::TinyAsciiStr;
471
472 const MAX_NICKNAME_LEN: usize = 19;
474
475 #[cfg_attr(docsrs, doc(cfg(feature = "dangerous-expose-struct-fields")))]
484 #[cfg_attr(feature = "dangerous-expose-struct-fields", visibility::make(pub))]
485 #[derive(Clone, Debug)]
486 pub(crate) struct Nickname(tinystr::TinyAsciiStr<MAX_NICKNAME_LEN>);
487
488 impl Nickname {
489 pub(crate) fn as_str(&self) -> &str {
491 self.0.as_str()
492 }
493 }
494
495 impl std::fmt::Display for Nickname {
496 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
497 self.as_str().fmt(f)
498 }
499 }
500
501 impl std::str::FromStr for Nickname {
502 type Err = Error;
503
504 fn from_str(s: &str) -> Result<Self> {
505 let tiny = TinyAsciiStr::from_str(s).map_err(|_| {
506 EK::BadArgument
507 .at_pos(Pos::at(s))
508 .with_msg("Invalid nickname")
509 })?;
510
511 if tiny.is_ascii_alphanumeric() && !tiny.is_empty() {
512 Ok(Nickname(tiny))
513 } else {
514 Err(EK::BadArgument
515 .at_pos(Pos::at(s))
516 .with_msg("Invalid nickname"))
517 }
518 }
519 }
520}
521
522#[cfg(test)]
523mod test {
524 #![allow(clippy::bool_assert_comparison)]
526 #![allow(clippy::clone_on_copy)]
527 #![allow(clippy::dbg_macro)]
528 #![allow(clippy::mixed_attributes_style)]
529 #![allow(clippy::print_stderr)]
530 #![allow(clippy::print_stdout)]
531 #![allow(clippy::single_char_pattern)]
532 #![allow(clippy::unwrap_used)]
533 #![allow(clippy::unchecked_duration_subtraction)]
534 #![allow(clippy::useless_vec)]
535 #![allow(clippy::needless_pass_by_value)]
536 use itertools::Itertools;
538
539 use base64ct::Encoding;
540
541 use super::*;
542 use crate::{Pos, Result};
543
544 fn base64_decode_ignore_ws(s: &str) -> std::result::Result<Vec<u8>, base64ct::Error> {
546 let mut s = s.to_string();
547 s.retain(|c| !c.is_ascii_whitespace());
548 base64ct::Base64::decode_vec(s.as_str())
549 }
550
551 #[test]
552 fn base64() -> Result<()> {
553 assert_eq!("Mi43MTgyOA".parse::<B64>()?.as_bytes(), &b"2.71828"[..]);
556 assert!("Mi43MTgyOA".parse::<B64>()?.check_len(7..8).is_ok());
557 assert_eq!("Mg".parse::<B64>()?.as_bytes(), &b"2"[..]);
558 assert!("Mg".parse::<B64>()?.check_len(1..2).is_ok());
559 assert_eq!(
560 "8J+NkvCfjZLwn42S8J+NkvCfjZLwn42S"
561 .parse::<B64>()?
562 .as_bytes(),
563 "๐๐๐๐๐๐".as_bytes()
564 );
565 assert!("8J+NkvCfjZLwn42S8J+NkvCfjZLwn42S"
566 .parse::<B64>()?
567 .check_len(24..25)
568 .is_ok());
569 assert!("ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz8="
570 .parse::<B64>()?
571 .check_len(32..33)
572 .is_ok());
573 assert_eq!("Mi43MTgyOA==".parse::<B64>()?.as_bytes(), &b"2.71828"[..]);
575 assert!("Mi43MTgyOA==".parse::<B64>()?.check_len(7..8).is_ok());
576 assert_eq!("Mg==".parse::<B64>()?.as_bytes(), &b"2"[..]);
577 assert!("Mg==".parse::<B64>()?.check_len(1..2).is_ok());
578
579 assert!("Mi43!!!!!!".parse::<B64>().is_err());
582 assert!("Mi".parse::<B64>().is_err());
584 assert!("ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxaaaa"
585 .parse::<B64>()
586 .is_err());
587 assert!("Mi43MTgyOA".parse::<B64>()?.check_len(8..).is_err());
589 Ok(())
590 }
591
592 #[test]
593 fn base64_lengths() -> Result<()> {
594 assert_eq!("".parse::<B64>()?.as_bytes(), b"");
595 assert!("=".parse::<B64>().is_err());
596 assert!("==".parse::<B64>().is_err());
597 assert!("B".parse::<B64>().is_err());
598 assert!("B=".parse::<B64>().is_err());
599 assert!("B==".parse::<B64>().is_err());
600 assert!("Bg=".parse::<B64>().is_err());
601 assert_eq!("Bg".parse::<B64>()?.as_bytes(), b"\x06");
602 assert_eq!("Bg==".parse::<B64>()?.as_bytes(), b"\x06");
603 assert_eq!("BCg".parse::<B64>()?.as_bytes(), b"\x04\x28");
604 assert_eq!("BCg=".parse::<B64>()?.as_bytes(), b"\x04\x28");
605 assert!("BCg==".parse::<B64>().is_err());
606 assert_eq!("BCDE".parse::<B64>()?.as_bytes(), b"\x04\x20\xc4");
607 assert!("BCDE=".parse::<B64>().is_err());
608 assert!("BCDE==".parse::<B64>().is_err());
609 Ok(())
610 }
611
612 #[test]
613 fn base64_rev() {
614 use base64ct::{Base64, Base64Unpadded};
615
616 for n in 0..=5 {
619 for c_vec in std::iter::repeat_n("ACEQg/=".chars(), n).multi_cartesian_product() {
620 let s: String = c_vec.into_iter().collect();
621 #[allow(clippy::print_stderr)]
622 let b = match s.parse::<B64>() {
623 Ok(b) => {
624 eprintln!("{:10} {:?}", &s, b.as_bytes());
625 b
626 }
627 Err(_) => {
628 eprintln!("{:10} Err", &s);
629 continue;
630 }
631 };
632 let b = b.as_bytes();
633
634 let ep = Base64::encode_string(b);
635 let eu = Base64Unpadded::encode_string(b);
636
637 assert!(
638 s == ep || s == eu,
639 "{:?} decoded to {:?} giving neither {:?} nor {:?}",
640 s,
641 b,
642 ep,
643 eu
644 );
645 }
646 }
647 }
648
649 #[test]
650 fn base16() -> Result<()> {
651 assert_eq!("332e313432".parse::<B16>()?.as_bytes(), &b"3.142"[..]);
652 assert_eq!("332E313432".parse::<B16>()?.as_bytes(), &b"3.142"[..]);
653 assert_eq!("332E3134".parse::<B16>()?.as_bytes(), &b"3.14"[..]);
654 assert!("332E313".parse::<B16>().is_err());
655 assert!("332G3134".parse::<B16>().is_err());
656 Ok(())
657 }
658
659 #[test]
660 fn curve25519() -> Result<()> {
661 use tor_llcrypto::pk::curve25519::PublicKey;
662 let k1 = "ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz8=";
663 let k2 = hex::decode("a69c2d8475d6f245c3d1ff5f13b50f62c38002ee2e8f9391c12a2608cc4a933f")
664 .unwrap();
665 let k2: &[u8; 32] = &k2[..].try_into().unwrap();
666
667 let k1: PublicKey = k1.parse::<Curve25519Public>()?.into();
668 assert_eq!(k1, (*k2).into());
669
670 assert!("ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxKkz"
671 .parse::<Curve25519Public>()
672 .is_err());
673 assert!("ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORSomCMxKkz"
674 .parse::<Curve25519Public>()
675 .is_err());
676 assert!("ppwthHXW8kXD0f9fE7UPYsOAAu4uj5wSomCMxKkz"
677 .parse::<Curve25519Public>()
678 .is_err());
679 assert!("ppwthHXW8kXD0f9fE7UPYsOAAu4ORwSomCMxKkz"
680 .parse::<Curve25519Public>()
681 .is_err());
682
683 Ok(())
684 }
685
686 #[test]
687 fn ed25519() -> Result<()> {
688 use tor_llcrypto::pk::ed25519::Ed25519Identity;
689 let k1 = "WVIPQ8oArAqLY4XzkcpIOI6U8KsUJHBQhG8SC57qru0";
690 let k2 = hex::decode("59520f43ca00ac0a8b6385f391ca48388e94f0ab14247050846f120b9eeaaeed")
691 .unwrap();
692
693 let k1: Ed25519Identity = k1.parse::<Ed25519Public>()?.into();
694 assert_eq!(k1, Ed25519Identity::from_bytes(&k2).unwrap());
695
696 assert!("WVIPQ8oArAqLY4Xzk0!!!!8KsUJHBQhG8SC57qru"
697 .parse::<Ed25519Public>()
698 .is_err());
699 assert!("WVIPQ8oArAqLY4XzkcpIU8KsUJHBQhG8SC57qru"
700 .parse::<Ed25519Public>()
701 .is_err());
702 assert!("WVIPQ8oArAqLY4XzkcpIU8KsUJHBQhG8SC57qr"
703 .parse::<Ed25519Public>()
704 .is_err());
705 assert!("ppwthHXW8kXD0f9fE7UPYsOAAu4uj5ORwSomCMxaaaa"
707 .parse::<Curve25519Public>()
708 .is_err());
709 Ok(())
710 }
711
712 #[test]
713 fn time() -> Result<()> {
714 use humantime::parse_rfc3339;
715 use std::time::SystemTime;
716
717 let t = "2020-09-29 13:36:33".parse::<Iso8601TimeSp>()?;
718 let t: SystemTime = t.into();
719 assert_eq!(t, parse_rfc3339("2020-09-29T13:36:33Z").unwrap());
720
721 assert!("2020-FF-29 13:36:33".parse::<Iso8601TimeSp>().is_err());
722 assert!("2020-09-29Q13:99:33".parse::<Iso8601TimeSp>().is_err());
723 assert!("2020-09-29".parse::<Iso8601TimeSp>().is_err());
724 assert!("too bad, waluigi time".parse::<Iso8601TimeSp>().is_err());
725
726 assert_eq!(
727 "2020-09-29 13:36:33",
728 "2020-09-29 13:36:33".parse::<Iso8601TimeSp>()?.to_string()
729 );
730
731 let t = "2020-09-29T13:36:33".parse::<Iso8601TimeNoSp>()?;
732 let t: SystemTime = t.into();
733 assert_eq!(t, parse_rfc3339("2020-09-29T13:36:33Z").unwrap());
734
735 assert!("2020-09-29 13:36:33".parse::<Iso8601TimeNoSp>().is_err());
736 assert!("2020-09-29Q13:99:33".parse::<Iso8601TimeNoSp>().is_err());
737 assert!("2020-09-29".parse::<Iso8601TimeNoSp>().is_err());
738 assert!("too bad, waluigi time".parse::<Iso8601TimeNoSp>().is_err());
739
740 assert_eq!(
741 "2020-09-29T13:36:33",
742 "2020-09-29T13:36:33"
743 .parse::<Iso8601TimeNoSp>()?
744 .to_string()
745 );
746
747 Ok(())
748 }
749
750 #[test]
751 fn rsa_public_key() {
752 let key_b64 = r#"
754 MIIBigKCAYEAsDkzTcKS4kAF56R2ijb9qCek53tKC1EwMdpWMk58bB28fY6kHc55
755 E7n1hB+LC5neZlx88GKuZ9k8P3g0MlO5ejalcfBdIIm28Nz86JXf/L23YnEpxnG/
756 IpxZEcmx/EYN+vwp72W3DGuzyntaoaut6lGJk+O/aRCLLcTm4MNznvN1ackK2H6b
757 Xm2ejRwtVRLoPKODJiPGl43snCfXXWsMH3IALFOgm0szPLv2fAJzBI8VWrUN81M/
758 lgwJhG6+xbr1CkrXI5fKs/TNr0B0ydC9BIZplmPrnXaeNklnw1cqUJ1oxDSgBrvx
759 rpDo7paObjSPV26opa68QKGa7Gu2MZQC3RzViNCbawka/108g6hSUkoM+Om2oivr
760 DvtMOs10MjsfibEBVnwEhqnlb/gj3hJkYoGRsCwAyMIaMObHcmAevMJRWAjGCc8T
761 GMS9dSmg1IZst+U+V2OCcIHXT6wZ1zPsBM0pYKVLCwtewaq1306k0n+ekriEo7eI
762 FS3Dd/Dx/a6jAgMBAAE=
763 "#;
764 let key_bytes = base64_decode_ignore_ws(key_b64).unwrap();
765 let rsa = RsaPublic::from_vec(key_bytes, Pos::None).unwrap();
766
767 let bits = tor_llcrypto::pk::rsa::PublicKey::from(rsa.clone()).bits();
768 assert_eq!(bits, 3072);
769
770 assert!(rsa.clone().check_exponent(65537).is_ok());
772 assert!(rsa.clone().check_exponent(1337).is_err());
773 assert!(rsa.clone().check_len_eq(3072).is_ok());
774 assert!(rsa.clone().check_len(1024..=4096).is_ok());
775 assert!(rsa.clone().check_len(1024..=1024).is_err());
776 assert!(rsa.check_len(4096..).is_err());
777
778 let failure = RsaPublic::from_vec(vec![1, 2, 3], Pos::None);
780 assert!(failure.is_err());
781 }
782
783 #[cfg(feature = "routerdesc")]
784 #[test]
785 fn ed_cert() {
786 use tor_llcrypto::pk::ed25519::Ed25519Identity;
787
788 let cert_b64 = r#"
790 AQQABwRNAR6m3kq5h8i3wwac+Ti293opoOP8RKGP9MT0WD4Bbz7YAQAgBACGCdys
791 G7AwsoYMIKenDN6In6ReiGF8jaYoGqmWKDVBdGGMDIZyNIq+VdhgtAB1EyNFHJU1
792 jGM0ir9dackL+PIsHbzJH8s/P/8RfUsKIL6/ZHbn3nKMxLH/8kjtxp5ScAA=
793 "#;
794 let cert_bytes = base64_decode_ignore_ws(cert_b64).unwrap();
795 let right_subject_key: Ed25519Identity = "HqbeSrmHyLfDBpz5OLb3eimg4/xEoY/0xPRYPgFvPtg"
797 .parse::<Ed25519Public>()
798 .unwrap()
799 .into();
800 let wrong_subject_key: Ed25519Identity = "WVIPQ8oArAqLY4XzkcpIOI6U8KsUJHBQhG8SC57qru0"
802 .parse::<Ed25519Public>()
803 .unwrap()
804 .into();
805
806 let cert = UnvalidatedEdCert::from_vec(cert_bytes, Pos::None)
808 .unwrap()
809 .check_cert_type(tor_cert::CertType::IDENTITY_V_SIGNING)
810 .unwrap()
811 .check_subject_key_is(&right_subject_key)
812 .unwrap();
813 assert!(cert
815 .clone()
816 .check_cert_type(tor_cert::CertType::RSA_ID_X509)
817 .is_err());
818 assert!(cert.check_subject_key_is(&wrong_subject_key).is_err());
820
821 let failure = UnvalidatedEdCert::from_vec(vec![1, 2, 3], Pos::None);
823 assert!(failure.is_err());
824 }
825
826 #[test]
827 fn fingerprint() -> Result<()> {
828 use tor_llcrypto::pk::rsa::RsaIdentity;
829 let fp1 = "7467 A97D 19CD 2B4F 2BC0 388A A99C 5E67 710F 847E";
830 let fp2 = "7467A97D19CD2B4F2BC0388AA99C5E67710F847E";
831 let fp3 = "$7467A97D19CD2B4F2BC0388AA99C5E67710F847E";
832 let fp4 = "$7467A97D19CD2B4F2BC0388AA99C5E67710F847E=fred";
833
834 let k = hex::decode(fp2).unwrap();
835 let k = RsaIdentity::from_bytes(&k[..]).unwrap();
836
837 assert_eq!(RsaIdentity::from(fp1.parse::<SpFingerprint>()?), k);
838 assert_eq!(RsaIdentity::from(fp2.parse::<SpFingerprint>()?), k);
839 assert!(fp3.parse::<SpFingerprint>().is_err());
840 assert!(fp4.parse::<SpFingerprint>().is_err());
841
842 assert!(fp1.parse::<Fingerprint>().is_err());
843 assert_eq!(RsaIdentity::from(fp2.parse::<Fingerprint>()?), k);
844 assert!(fp3.parse::<Fingerprint>().is_err());
845 assert!(fp4.parse::<Fingerprint>().is_err());
846
847 assert!(fp1.parse::<LongIdent>().is_err());
848 assert_eq!(RsaIdentity::from(fp2.parse::<LongIdent>()?), k);
849 assert_eq!(RsaIdentity::from(fp3.parse::<LongIdent>()?), k);
850 assert_eq!(RsaIdentity::from(fp4.parse::<LongIdent>()?), k);
851
852 assert!("xxxx".parse::<Fingerprint>().is_err());
853 assert!("ffffffffff".parse::<Fingerprint>().is_err());
854 Ok(())
855 }
856
857 #[test]
858 fn nickname() -> Result<()> {
859 let n: Nickname = "Foo".parse()?;
860 assert_eq!(n.as_str(), "Foo");
861 assert_eq!(n.to_string(), "Foo");
862
863 let word = "Untr1gonometr1cally";
864 assert_eq!(word.len(), 19);
865 let long: Nickname = word.parse()?;
866 assert_eq!(long.as_str(), word);
867
868 let too_long = "abcdefghijklmnopqrstuvwxyz";
869 let not_ascii = "Eyjafjallajรถkull";
870 let too_short = "";
871 let other_invalid = "contains space";
872 assert!(not_ascii.len() <= 19);
873 assert!(too_long.parse::<Nickname>().is_err());
874 assert!(not_ascii.parse::<Nickname>().is_err());
875 assert!(too_short.parse::<Nickname>().is_err());
876 assert!(other_invalid.parse::<Nickname>().is_err());
877
878 Ok(())
879 }
880}