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