1
//! Code to abstract over the notion of relays having one or more identities.
2
//!
3
//! Currently (2022), every Tor relay has exactly two identities: A legacy
4
//! identity that is based on the SHA-1 hash of an RSA-1024 public key, and a
5
//! modern identity that is an Ed25519 public key.  This code lets us abstract
6
//! over those types, and over other new types that may exist in the future.
7

            
8
use std::fmt;
9

            
10
use derive_deftly::Deftly;
11
use derive_more::{Display, From};
12
use safelog::Redactable;
13
use tor_llcrypto::pk::{
14
    ed25519::{Ed25519Identity, ED25519_ID_LEN},
15
    rsa::{RsaIdentity, RSA_ID_LEN},
16
};
17

            
18
pub(crate) mod by_id;
19
pub(crate) mod set;
20

            
21
/// The type of a relay identity.
22
///
23
#[derive(
24
    Debug,
25
    Clone,
26
    Copy,
27
    Eq,
28
    PartialEq,
29
    Hash,
30
    Ord,
31
    PartialOrd,
32
    Display,
33
206443622
    strum::EnumIter,
34
    strum::EnumCount,
35
    Deftly,
36
)]
37
#[derive_deftly_adhoc]
38
#[non_exhaustive]
39
pub enum RelayIdType {
40
    /// An Ed25519 identity.
41
    ///
42
    /// Every relay (currently) has one of these identities. It is the same
43
    /// as the encoding of the relay's public Ed25519 identity key.
44
    #[display("Ed25519")]
45
    Ed25519,
46
    /// An RSA identity.
47
    ///
48
    /// Every relay (currently) has one of these identities.  It is computed as
49
    /// a SHA-1 digest of the DER encoding of the relay's public RSA 1024-bit
50
    /// identity key.  Because of short key length, this type of identity should
51
    /// not be considered secure on its own.
52
    #[display("RSA (legacy)")]
53
    Rsa,
54
}
55

            
56
impl fmt::Display for RelayId {
57
24
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
58
24
        fmt::Display::fmt(&self.as_ref(), f)
59
24
    }
60
}
61

            
62
/// A single relay identity.
63
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, From, Hash)]
64
#[non_exhaustive]
65
pub enum RelayId {
66
    /// An Ed25519 identity.
67
    Ed25519(Ed25519Identity),
68
    /// An RSA identity.
69
    Rsa(RsaIdentity),
70
}
71

            
72
/// A reference to a single relay identity.
73
#[derive(
74
    Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Display, From, derive_more::TryInto,
75
)]
76
#[non_exhaustive]
77
pub enum RelayIdRef<'a> {
78
    /// An Ed25519 identity.
79
    #[display("ed25519:{}", _0)]
80
    Ed25519(&'a Ed25519Identity),
81
    /// An RSA identity.
82
    #[display("{}", _0)]
83
    Rsa(&'a RsaIdentity),
84
}
85

            
86
impl RelayIdType {
87
    /// The number of distinct types currently implemented.
88
    pub const COUNT: usize = <RelayIdType as strum::EnumCount>::COUNT;
89

            
90
    /// Return an iterator over all
91
48034522
    pub fn all_types() -> RelayIdTypeIter {
92
        use strum::IntoEnumIterator;
93
48034522
        Self::iter()
94
48034522
    }
95

            
96
    /// Return the length of this identity, in bytes.
97
1551
    pub fn id_len(&self) -> usize {
98
1551
        match self {
99
1549
            RelayIdType::Ed25519 => ED25519_ID_LEN,
100
2
            RelayIdType::Rsa => RSA_ID_LEN,
101
        }
102
1551
    }
103
}
104

            
105
impl RelayId {
106
    /// Return a [`RelayIdRef`] pointing to the contents of this identity.
107
5258
    pub fn as_ref(&self) -> RelayIdRef<'_> {
108
5258
        match self {
109
3113
            RelayId::Ed25519(key) => key.into(),
110
2145
            RelayId::Rsa(key) => key.into(),
111
        }
112
5258
    }
113

            
114
    /// Try to construct a RelayId of a provided `id_type` from a byte-slice.
115
    ///
116
    /// Return [`RelayIdError::BadLength`] if the slice is not the correct length for the key.
117
5948
    pub fn from_type_and_bytes(id_type: RelayIdType, id: &[u8]) -> Result<Self, RelayIdError> {
118
5948
        Ok(match id_type {
119
4201
            RelayIdType::Rsa => RsaIdentity::from_bytes(id)
120
4201
                .ok_or(RelayIdError::BadLength)?
121
4201
                .into(),
122
1747
            RelayIdType::Ed25519 => Ed25519Identity::from_bytes(id)
123
1747
                .ok_or(RelayIdError::BadLength)?
124
1743
                .into(),
125
        })
126
5948
    }
127

            
128
    /// Return the type of this relay identity.
129
4
    pub fn id_type(&self) -> RelayIdType {
130
4
        self.as_ref().id_type()
131
4
    }
132

            
133
    /// Return a byte-slice corresponding to the contents of this identity.
134
    ///
135
    /// The return value discards the type of the identity, and so should be
136
    /// handled with care to make sure that it does not get confused with an
137
    /// identity of some other type.
138
4
    pub fn as_bytes(&self) -> &[u8] {
139
4
        self.as_ref().as_bytes()
140
4
    }
141
}
142

            
143
impl<'a> RelayIdRef<'a> {
144
    /// Copy this reference into a new [`RelayId`] object.
145
    //
146
    // TODO(nickm): I wish I could make this a proper `ToOwned` implementation,
147
    // but I see no way to do as long as RelayIdRef<'a> implements Clone too.
148
294845
    pub fn to_owned(&self) -> RelayId {
149
294845
        match *self {
150
147200
            RelayIdRef::Ed25519(key) => (*key).into(),
151
147645
            RelayIdRef::Rsa(key) => (*key).into(),
152
        }
153
294845
    }
154

            
155
    /// Return the type of this relay identity.
156
857459
    pub fn id_type(&self) -> RelayIdType {
157
857459
        match self {
158
429636
            RelayIdRef::Ed25519(_) => RelayIdType::Ed25519,
159
427823
            RelayIdRef::Rsa(_) => RelayIdType::Rsa,
160
        }
161
857459
    }
162

            
163
    /// Return a byte-slice corresponding to the contents of this identity.
164
490106
    pub fn as_bytes(&self) -> &'a [u8] {
165
490106
        match self {
166
53
            RelayIdRef::Ed25519(key) => key.as_bytes(),
167
490053
            RelayIdRef::Rsa(key) => key.as_bytes(),
168
        }
169
490106
    }
170

            
171
    /// Extract the RsaIdentity from a RelayIdRef that is known to hold one.
172
    ///
173
    /// # Panics
174
    ///
175
    /// Panics if this is not an RSA identity.
176
15904461
    pub(crate) fn unwrap_rsa(self) -> &'a RsaIdentity {
177
15904461
        match self {
178
15904459
            RelayIdRef::Rsa(rsa) => rsa,
179
2
            _ => panic!("Not an RSA identity."),
180
        }
181
15904459
    }
182

            
183
    /// Extract the Ed25519Identity from a RelayIdRef that is known to hold one.
184
    ///
185
    /// # Panics
186
    ///
187
    /// Panics if this is not an Ed25519 identity.
188
15550077
    pub(crate) fn unwrap_ed25519(self) -> &'a Ed25519Identity {
189
15550077
        match self {
190
15550075
            RelayIdRef::Ed25519(ed25519) => ed25519,
191
2
            _ => panic!("Not an Ed25519 identity."),
192
        }
193
15550075
    }
194
}
195

            
196
impl<'a> From<&'a RelayId> for RelayIdRef<'a> {
197
984
    fn from(ident: &'a RelayId) -> Self {
198
984
        ident.as_ref()
199
984
    }
200
}
201

            
202
impl 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

            
212
impl<'a> Redactable for RelayIdRef<'a> {
213
2
    fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
214
2
        match self {
215
2
            RelayIdRef::Ed25519(k) => write!(f, "ed25519:{}", k.redacted()),
216
            RelayIdRef::Rsa(k) => write!(f, "${}", k.redacted()),
217
        }
218
2
    }
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

            
229
/// Expand to an implementation for PartialEq for a given key type.
230
macro_rules! impl_eq_variant {
231
    { $var:ident($type:ty) } => {
232
        impl<'a> PartialEq<$type> for RelayIdRef<'a> {
233
1972
            fn eq(&self, other: &$type) -> bool {
234
1968
                matches!(self, RelayIdRef::$var(this) if this == &other)
235
1972
            }
236
        }
237
        impl PartialEq<$type> for RelayId {
238
12
            fn eq(&self, other: &$type) -> bool {
239
8
                matches!(&self, RelayId::$var(this) if this == other)
240
12
            }
241
        }
242
    }
243
}
244

            
245
impl_eq_variant! { Rsa(RsaIdentity) }
246
impl_eq_variant! { Ed25519(Ed25519Identity) }
247

            
248
impl std::str::FromStr for RelayIdType {
249
    type Err = RelayIdError;
250

            
251
1553
    fn from_str(s: &str) -> Result<Self, Self::Err> {
252
1553
        if s.eq_ignore_ascii_case("rsa") {
253
2
            Ok(RelayIdType::Rsa)
254
1551
        } else if s.eq_ignore_ascii_case("ed25519") {
255
1549
            Ok(RelayIdType::Ed25519)
256
        } else {
257
2
            Err(RelayIdError::UnrecognizedIdType)
258
        }
259
1553
    }
260
}
261

            
262
impl std::str::FromStr for RelayId {
263
    type Err = RelayIdError;
264

            
265
    /// Try to parse `s` as a RelayId.
266
    ///
267
    /// We use the following format, based on the one used by C tor.
268
    ///
269
    /// * An optional `$` followed by a 40 byte hex string is always an RSA key.
270
    /// * A 43 character un-padded base-64 string is always an Ed25519 key.
271
    /// * The name of an algorithm ("rsa" or "ed25519"), followed by a colon and
272
    ///   and an un-padded base-64 string is a key of that type.
273
7081
    fn from_str(s: &str) -> Result<Self, Self::Err> {
274
        use base64ct::{Base64Unpadded, Encoding as _};
275
7081
        if let Some((alg, key)) = s.split_once(':') {
276
1553
            let alg: RelayIdType = alg.parse()?;
277
1551
            let len = alg.id_len();
278
1551
            let mut v = vec![0_u8; len];
279
1551
            let bytes = Base64Unpadded::decode(key, &mut v[..])?;
280
1549
            RelayId::from_type_and_bytes(alg, bytes)
281
5528
        } else if s.len() == RSA_ID_LEN * 2 || s.starts_with('$') {
282
4201
            let s = s.trim_start_matches('$');
283
4202
            let bytes = hex::decode(s).map_err(|_| RelayIdError::BadHex)?;
284
4199
            RelayId::from_type_and_bytes(RelayIdType::Rsa, &bytes)
285
        } else {
286
1327
            let mut v = [0_u8; ED25519_ID_LEN];
287
1327
            let bytes = Base64Unpadded::decode(s, &mut v[..])?;
288
200
            RelayId::from_type_and_bytes(RelayIdType::Ed25519, bytes)
289
        }
290
7081
    }
291
}
292

            
293
impl serde::Serialize for RelayId {
294
10
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
295
10
    where
296
10
        S: serde::Serializer,
297
10
    {
298
10
        self.as_ref().serialize(serializer)
299
10
    }
300
}
301
impl<'a> serde::Serialize for RelayIdRef<'a> {
302
14
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
303
14
    where
304
14
        S: serde::Serializer,
305
14
    {
306
14
        // TODO(nickm): maybe encode this as bytes when dealing with
307
14
        // non-human-readable formats.
308
14
        self.to_string().serialize(serializer)
309
14
    }
310
}
311

            
312
impl<'de> serde::Deserialize<'de> for RelayId {
313
28
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
314
28
    where
315
28
        D: serde::Deserializer<'de>,
316
28
    {
317
        // TODO(nickm): maybe allow bytes when dealing with non-human-readable
318
        // formats.
319
        use serde::de::Error as _;
320
28
        let s = <std::borrow::Cow<'_, str> as serde::Deserialize>::deserialize(deserializer)?;
321
28
        s.parse()
322
28
            .map_err(|e: RelayIdError| D::Error::custom(e.to_string()))
323
28
    }
324
}
325

            
326
/// An error returned while trying to parse a RelayId.
327
#[derive(Clone, Debug, thiserror::Error)]
328
#[non_exhaustive]
329
pub enum RelayIdError {
330
    /// We didn't recognize the type of a relay identity.
331
    ///
332
    /// This can happen when a type that we have never heard of is specified, or when a type
333
    #[error("Unrecognized type for relay identity")]
334
    UnrecognizedIdType,
335
    /// We encountered base64 data that we couldn't parse.
336
    #[error("Invalid base64 data")]
337
    BadBase64,
338
    /// We encountered hex data that we couldn't parse.
339
    #[error("Invalid hexadecimal data")]
340
    BadHex,
341
    /// We got a key that was the wrong length.
342
    #[error("Invalid length for relay identity")]
343
    BadLength,
344
}
345

            
346
impl From<base64ct::Error> for RelayIdError {
347
1129
    fn from(err: base64ct::Error) -> Self {
348
1129
        match err {
349
982
            base64ct::Error::InvalidEncoding => RelayIdError::BadBase64,
350
147
            base64ct::Error::InvalidLength => RelayIdError::BadLength,
351
        }
352
1129
    }
353
}
354

            
355
#[cfg(test)]
356
mod test {
357
    // @@ begin test lint list maintained by maint/add_warning @@
358
    #![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
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
370
    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
        // Try a few RSA identities.
394
        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
        // Try a few ed25519 identities
409
        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
}