1use std::fmt;
9
10use derive_deftly::Deftly;
11use derive_more::{Display, From};
12use safelog::Redactable;
13use tor_llcrypto::pk::{
14 ed25519::{Ed25519Identity, ED25519_ID_LEN},
15 rsa::{RsaIdentity, RSA_ID_LEN},
16};
17
18pub(crate) mod by_id;
19pub(crate) mod set;
20
21#[derive(
24 Debug,
25 Clone,
26 Copy,
27 Eq,
28 PartialEq,
29 Hash,
30 Ord,
31 PartialOrd,
32 Display,
33 strum::EnumIter,
34 strum::EnumCount,
35 Deftly,
36)]
37#[derive_deftly_adhoc]
38#[non_exhaustive]
39pub enum RelayIdType {
40 #[display("Ed25519")]
45 Ed25519,
46 #[display("RSA (legacy)")]
53 Rsa,
54}
55
56impl fmt::Display for RelayId {
57 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
58 fmt::Display::fmt(&self.as_ref(), f)
59 }
60}
61
62#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, From, Hash)]
64#[non_exhaustive]
65pub enum RelayId {
66 Ed25519(Ed25519Identity),
68 Rsa(RsaIdentity),
70}
71
72#[derive(
74 Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Display, From, derive_more::TryInto,
75)]
76#[non_exhaustive]
77pub enum RelayIdRef<'a> {
78 #[display("ed25519:{}", _0)]
80 Ed25519(&'a Ed25519Identity),
81 #[display("{}", _0)]
83 Rsa(&'a RsaIdentity),
84}
85
86impl RelayIdType {
87 pub const COUNT: usize = <RelayIdType as strum::EnumCount>::COUNT;
89
90 pub fn all_types() -> RelayIdTypeIter {
92 use strum::IntoEnumIterator;
93 Self::iter()
94 }
95
96 pub fn id_len(&self) -> usize {
98 match self {
99 RelayIdType::Ed25519 => ED25519_ID_LEN,
100 RelayIdType::Rsa => RSA_ID_LEN,
101 }
102 }
103}
104
105impl RelayId {
106 pub fn as_ref(&self) -> RelayIdRef<'_> {
108 match self {
109 RelayId::Ed25519(key) => key.into(),
110 RelayId::Rsa(key) => key.into(),
111 }
112 }
113
114 pub fn from_type_and_bytes(id_type: RelayIdType, id: &[u8]) -> Result<Self, RelayIdError> {
118 Ok(match id_type {
119 RelayIdType::Rsa => RsaIdentity::from_bytes(id)
120 .ok_or(RelayIdError::BadLength)?
121 .into(),
122 RelayIdType::Ed25519 => Ed25519Identity::from_bytes(id)
123 .ok_or(RelayIdError::BadLength)?
124 .into(),
125 })
126 }
127
128 pub fn id_type(&self) -> RelayIdType {
130 self.as_ref().id_type()
131 }
132
133 pub fn as_bytes(&self) -> &[u8] {
139 self.as_ref().as_bytes()
140 }
141}
142
143impl<'a> RelayIdRef<'a> {
144 pub fn to_owned(&self) -> RelayId {
149 match *self {
150 RelayIdRef::Ed25519(key) => (*key).into(),
151 RelayIdRef::Rsa(key) => (*key).into(),
152 }
153 }
154
155 pub fn id_type(&self) -> RelayIdType {
157 match self {
158 RelayIdRef::Ed25519(_) => RelayIdType::Ed25519,
159 RelayIdRef::Rsa(_) => RelayIdType::Rsa,
160 }
161 }
162
163 pub fn as_bytes(&self) -> &'a [u8] {
165 match self {
166 RelayIdRef::Ed25519(key) => key.as_bytes(),
167 RelayIdRef::Rsa(key) => key.as_bytes(),
168 }
169 }
170
171 pub(crate) fn unwrap_rsa(self) -> &'a RsaIdentity {
177 match self {
178 RelayIdRef::Rsa(rsa) => rsa,
179 _ => panic!("Not an RSA identity."),
180 }
181 }
182
183 pub(crate) fn unwrap_ed25519(self) -> &'a Ed25519Identity {
189 match self {
190 RelayIdRef::Ed25519(ed25519) => ed25519,
191 _ => panic!("Not an Ed25519 identity."),
192 }
193 }
194}
195
196impl<'a> From<&'a RelayId> for RelayIdRef<'a> {
197 fn from(ident: &'a RelayId) -> Self {
198 ident.as_ref()
199 }
200}
201
202impl Redactable for RelayId {
203 fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204 self.as_ref().display_redacted(f)
205 }
206
207 fn debug_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208 self.as_ref().debug_redacted(f)
209 }
210}
211
212impl<'a> Redactable for RelayIdRef<'a> {
213 fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
214 match self {
215 RelayIdRef::Ed25519(k) => write!(f, "ed25519:{}", k.redacted()),
216 RelayIdRef::Rsa(k) => write!(f, "${}", k.redacted()),
217 }
218 }
219
220 fn debug_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
221 use std::fmt::Debug;
222 match self {
223 RelayIdRef::Ed25519(k) => Debug::fmt(*k.redacted(), f),
224 RelayIdRef::Rsa(k) => Debug::fmt(*k.redacted(), f),
225 }
226 }
227}
228
229macro_rules! impl_eq_variant {
231 { $var:ident($type:ty) } => {
232 impl<'a> PartialEq<$type> for RelayIdRef<'a> {
233 fn eq(&self, other: &$type) -> bool {
234 matches!(self, RelayIdRef::$var(this) if this == &other)
235 }
236 }
237 impl PartialEq<$type> for RelayId {
238 fn eq(&self, other: &$type) -> bool {
239 matches!(&self, RelayId::$var(this) if this == other)
240 }
241 }
242 }
243}
244
245impl_eq_variant! { Rsa(RsaIdentity) }
246impl_eq_variant! { Ed25519(Ed25519Identity) }
247
248impl std::str::FromStr for RelayIdType {
249 type Err = RelayIdError;
250
251 fn from_str(s: &str) -> Result<Self, Self::Err> {
252 if s.eq_ignore_ascii_case("rsa") {
253 Ok(RelayIdType::Rsa)
254 } else if s.eq_ignore_ascii_case("ed25519") {
255 Ok(RelayIdType::Ed25519)
256 } else {
257 Err(RelayIdError::UnrecognizedIdType)
258 }
259 }
260}
261
262impl std::str::FromStr for RelayId {
263 type Err = RelayIdError;
264
265 fn from_str(s: &str) -> Result<Self, Self::Err> {
274 use base64ct::{Base64Unpadded, Encoding as _};
275 if let Some((alg, key)) = s.split_once(':') {
276 let alg: RelayIdType = alg.parse()?;
277 let len = alg.id_len();
278 let mut v = vec![0_u8; len];
279 let bytes = Base64Unpadded::decode(key, &mut v[..])?;
280 RelayId::from_type_and_bytes(alg, bytes)
281 } else if s.len() == RSA_ID_LEN * 2 || s.starts_with('$') {
282 let s = s.trim_start_matches('$');
283 let bytes = hex::decode(s).map_err(|_| RelayIdError::BadHex)?;
284 RelayId::from_type_and_bytes(RelayIdType::Rsa, &bytes)
285 } else {
286 let mut v = [0_u8; ED25519_ID_LEN];
287 let bytes = Base64Unpadded::decode(s, &mut v[..])?;
288 RelayId::from_type_and_bytes(RelayIdType::Ed25519, bytes)
289 }
290 }
291}
292
293impl serde::Serialize for RelayId {
294 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
295 where
296 S: serde::Serializer,
297 {
298 self.as_ref().serialize(serializer)
299 }
300}
301impl<'a> serde::Serialize for RelayIdRef<'a> {
302 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
303 where
304 S: serde::Serializer,
305 {
306 self.to_string().serialize(serializer)
309 }
310}
311
312impl<'de> serde::Deserialize<'de> for RelayId {
313 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
314 where
315 D: serde::Deserializer<'de>,
316 {
317 use serde::de::Error as _;
320 let s = <std::borrow::Cow<'_, str> as serde::Deserialize>::deserialize(deserializer)?;
321 s.parse()
322 .map_err(|e: RelayIdError| D::Error::custom(e.to_string()))
323 }
324}
325
326#[derive(Clone, Debug, thiserror::Error)]
328#[non_exhaustive]
329pub enum RelayIdError {
330 #[error("Unrecognized type for relay identity")]
334 UnrecognizedIdType,
335 #[error("Invalid base64 data")]
337 BadBase64,
338 #[error("Invalid hexadecimal data")]
340 BadHex,
341 #[error("Invalid length for relay identity")]
343 BadLength,
344}
345
346impl From<base64ct::Error> for RelayIdError {
347 fn from(err: base64ct::Error) -> Self {
348 match err {
349 base64ct::Error::InvalidEncoding => RelayIdError::BadBase64,
350 base64ct::Error::InvalidLength => RelayIdError::BadLength,
351 }
352 }
353}
354
355#[cfg(test)]
356mod test {
357 #![allow(clippy::bool_assert_comparison)]
359 #![allow(clippy::clone_on_copy)]
360 #![allow(clippy::dbg_macro)]
361 #![allow(clippy::mixed_attributes_style)]
362 #![allow(clippy::print_stderr)]
363 #![allow(clippy::print_stdout)]
364 #![allow(clippy::single_char_pattern)]
365 #![allow(clippy::unwrap_used)]
366 #![allow(clippy::unchecked_duration_subtraction)]
367 #![allow(clippy::useless_vec)]
368 #![allow(clippy::needless_pass_by_value)]
369 use hex_literal::hex;
371 use serde_test::{assert_tokens, Token};
372 use std::str::FromStr;
373
374 use super::*;
375
376 #[test]
377 fn parse_and_display() -> Result<(), RelayIdError> {
378 fn normalizes_to(s: &str, expected: &str) -> Result<(), RelayIdError> {
379 let k: RelayId = s.parse()?;
380 let s2 = k.to_string();
381 assert_eq!(s2, expected);
382 let k2: RelayId = s2.parse()?;
383 let s3 = k2.to_string();
384 assert_eq!(s3, s2);
385 let s4 = k2.as_ref().to_string();
386 assert_eq!(s4, s3);
387 Ok(())
388 }
389 fn check(s: &str) -> Result<(), RelayIdError> {
390 normalizes_to(s, s)
391 }
392
393 check("$1234567812345678123456781234567812345678")?;
395 normalizes_to(
396 "abcdefabcdefabcdefabcdefabcdef1234567890",
397 "$abcdefabcdefabcdefabcdefabcdef1234567890",
398 )?;
399 normalizes_to(
400 "abcdefabcdefABCDEFabcdefabcdef1234567890",
401 "$abcdefabcdefabcdefabcdefabcdef1234567890",
402 )?;
403 normalizes_to(
404 "rsa:q83vq83vq83vq83vq83vEjRWeJA",
405 "$abcdefabcdefabcdefabcdefabcdef1234567890",
406 )?;
407
408 check("ed25519:dGhpcyBpcyBpbmNyZWRpYmx5IHNpbGx5ISEhISEhISE")?;
410 normalizes_to(
411 "dGhpcyBpcyBpbmNyZWRpYmx5IHNpbGx5ISEhISEhISE",
412 "ed25519:dGhpcyBpcyBpbmNyZWRpYmx5IHNpbGx5ISEhISEhISE",
413 )?;
414
415 Ok(())
416 }
417
418 #[test]
419 fn parse_fail() {
420 use std::str::FromStr;
421 let e = RelayId::from_str("tooshort").unwrap_err();
422 assert!(matches!(e, RelayIdError::BadLength));
423
424 let e = RelayId::from_str("this_string_is_40_bytes_but_it_isnt_hex!").unwrap_err();
425 assert!(matches!(e, RelayIdError::BadHex));
426
427 let e = RelayId::from_str("merkle-hellman:bestavoided").unwrap_err();
428 assert!(matches!(e, RelayIdError::UnrecognizedIdType));
429
430 let e = RelayId::from_str("ed25519:q83vq83vq83vq83vq83vEjRWeJA").unwrap_err();
431 assert!(matches!(e, RelayIdError::BadLength));
432
433 let e = RelayId::from_str("ed25519:🤨🤨🤨🤨🤨").unwrap_err();
434 assert!(matches!(e, RelayIdError::BadBase64));
435 }
436
437 #[test]
438 fn types() {
439 assert_eq!(
440 RelayId::from_str("$1234567812345678123456781234567812345678")
441 .unwrap()
442 .id_type(),
443 RelayIdType::Rsa,
444 );
445 assert_eq!(
446 RelayId::from_str("$1234567812345678123456781234567812345678")
447 .unwrap()
448 .as_ref()
449 .id_type(),
450 RelayIdType::Rsa,
451 );
452
453 assert_eq!(
454 RelayId::from_str("ed25519:dGhpcyBpcyBpbmNyZWRpYmx5IHNpbGx5ISEhISEhISE")
455 .unwrap()
456 .id_type(),
457 RelayIdType::Ed25519,
458 );
459
460 assert_eq!(
461 RelayId::from_str("ed25519:dGhpcyBpcyBpbmNyZWRpYmx5IHNpbGx5ISEhISEhISE")
462 .unwrap()
463 .as_ref()
464 .id_type(),
465 RelayIdType::Ed25519,
466 );
467 }
468
469 #[test]
470 fn equals_other() {
471 let rsa1 = RsaIdentity::from(*b"You just have to kno");
472 let rsa2 = RsaIdentity::from(*b"w who you are and st");
473 let ed1 = Ed25519Identity::from(*b"ay true to that. So I'm going to");
474 let ed2 = Ed25519Identity::from(*b"keep fighting for people the onl");
475
476 assert_eq!(RelayId::from(rsa1), rsa1);
477 assert_ne!(RelayId::from(rsa1), rsa2);
478 assert_ne!(RelayId::from(rsa1), ed1);
479
480 assert_eq!(RelayId::from(ed1), ed1);
481 assert_ne!(RelayId::from(ed1), ed2);
482 assert_ne!(RelayId::from(ed1), rsa1);
483
484 assert_eq!(RelayIdRef::from(&rsa1), rsa1);
485 assert_ne!(RelayIdRef::from(&rsa1), rsa2);
486 assert_ne!(RelayIdRef::from(&rsa1), ed1);
487
488 assert_eq!(RelayIdRef::from(&ed1), ed1);
489 assert_ne!(RelayIdRef::from(&ed1), ed2);
490 assert_ne!(RelayIdRef::from(&ed1), rsa1);
491 }
492 #[test]
493 fn as_bytes() {
494 assert_eq!(
495 RelayId::from_str("$1234567812345678123456781234567812345678")
496 .unwrap()
497 .as_bytes(),
498 hex!("1234567812345678123456781234567812345678"),
499 );
500 assert_eq!(
501 RelayId::from_str("$1234567812345678123456781234567812345678")
502 .unwrap()
503 .as_ref()
504 .as_bytes(),
505 hex!("1234567812345678123456781234567812345678"),
506 );
507
508 assert_eq!(
509 RelayId::from_str("ed25519:dGhpcyBpcyBpbmNyZWRpYmx5IHNpbGx5ISEhISEhISE")
510 .unwrap()
511 .as_bytes(),
512 b"this is incredibly silly!!!!!!!!"
513 );
514 assert_eq!(
515 RelayId::from_str("ed25519:dGhpcyBpcyBpbmNyZWRpYmx5IHNpbGx5ISEhISEhISE")
516 .unwrap()
517 .as_ref()
518 .as_bytes(),
519 b"this is incredibly silly!!!!!!!!"
520 );
521 }
522
523 #[test]
524 fn unwrap_ok() {
525 let rsa = RelayId::from_str("$1234567812345678123456781234567812345678").unwrap();
526 assert_eq!(
527 rsa.as_ref().unwrap_rsa(),
528 &RsaIdentity::from_bytes(&hex!("1234567812345678123456781234567812345678")).unwrap()
529 );
530
531 let ed = RelayId::from_str("ed25519:dGhpcyBpcyBpbmNyZWRpYmx5IHNpbGx5ISEhISEhISE").unwrap();
532 assert_eq!(
533 ed.as_ref().unwrap_ed25519(),
534 &Ed25519Identity::from_bytes(b"this is incredibly silly!!!!!!!!").unwrap()
535 );
536 }
537
538 #[test]
539 #[should_panic]
540 fn unwrap_rsa_panic() {
541 if let Ok(ed) = RelayId::from_str("ed25519:dGhpcyBpcyBpbmNyZWRpYmx5IHNpbGx5ISEhISEhISE") {
542 let _nope = RelayIdRef::from(&ed).unwrap_rsa();
543 }
544 }
545
546 #[test]
547 #[should_panic]
548 fn unwrap_ed_panic() {
549 if let Ok(ed) = RelayId::from_str("$1234567812345678123456781234567812345678") {
550 let _nope = RelayIdRef::from(&ed).unwrap_ed25519();
551 }
552 }
553
554 #[test]
555 fn serde_owned() {
556 let rsa1 = RsaIdentity::from(*b"You just have to kno");
557 let ed1 = Ed25519Identity::from(*b"ay true to that. So I'm going to");
558 let keys = vec![RelayId::from(rsa1), RelayId::from(ed1)];
559
560 assert_tokens(
561 &keys,
562 &[
563 Token::Seq { len: Some(2) },
564 Token::String("$596f75206a757374206861766520746f206b6e6f"),
565 Token::String("ed25519:YXkgdHJ1ZSB0byB0aGF0LiBTbyBJJ20gZ29pbmcgdG8"),
566 Token::SeqEnd,
567 ],
568 );
569 }
570}