1
//! Implements the relay 'family' type.
2
//!
3
//! Families are opt-in lists of relays with the same operators,
4
//! used to avoid building insecure circuits.
5

            
6
use std::sync::Arc;
7

            
8
use crate::types::misc::LongIdent;
9
use crate::{Error, NetdocErrorKind, NormalItemArgument, Pos, Result};
10
use base64ct::Encoding;
11
use derive_deftly::Deftly;
12
use tor_basic_utils::intern::InternCache;
13
use tor_llcrypto::pk::ed25519::{ED25519_ID_LEN, Ed25519Identity};
14
use tor_llcrypto::pk::rsa::RsaIdentity;
15

            
16
/// Information about a relay family.
17
///
18
/// Tor relays may declare that they belong to the same family, to
19
/// indicate that they are controlled by the same party or parties,
20
/// and as such should not be used in the same circuit. Two relays
21
/// belong to the same family if and only if each one lists the other
22
/// as belonging to its family.
23
///
24
/// NOTE: when parsing, this type always discards incorrectly-formatted
25
/// entries, including entries that are only nicknames.
26
///
27
/// TODO: This type probably belongs in a different crate.
28
#[derive(Clone, Debug, Default, Hash, Eq, PartialEq, Deftly)]
29
#[derive_deftly(ItemValueParseable)]
30
pub struct RelayFamily(Vec<LongIdent>);
31

            
32
/// Cache of RelayFamily objects, for saving memory.
33
//
34
/// This only holds weak references to the policy objects, so we don't
35
/// need to worry about running out of space because of stale entries.
36
static FAMILY_CACHE: InternCache<RelayFamily> = InternCache::new();
37

            
38
impl RelayFamily {
39
    /// Return a new empty RelayFamily.
40
480090
    pub fn new() -> Self {
41
480090
        RelayFamily::default()
42
480090
    }
43

            
44
    /// Add `rsa_id` to this family.
45
6
    pub fn push(&mut self, rsa_id: RsaIdentity) {
46
6
        self.0.push(rsa_id.into());
47
6
    }
48

            
49
    /// Convert this family to a standard format (with all IDs sorted and de-duplicated).
50
490898
    fn normalize(&mut self) {
51
490898
        self.0.sort_by_key(|v| v.0);
52
490898
        self.0.dedup();
53
490898
    }
54

            
55
    /// Consume this family, and return a new canonical interned representation
56
    /// of the family.
57
490898
    pub fn intern(mut self) -> Arc<Self> {
58
490898
        self.normalize();
59
490898
        FAMILY_CACHE.intern(self)
60
490898
    }
61

            
62
    /// Does this family include the given relay?
63
58723606
    pub fn contains(&self, rsa_id: &RsaIdentity) -> bool {
64
58723606
        self.0.contains(&LongIdent(*rsa_id))
65
58723606
    }
66

            
67
    /// Return an iterator over the RSA identity keys listed in this
68
    /// family.
69
114
    pub fn members(&self) -> impl Iterator<Item = &RsaIdentity> {
70
114
        self.0.iter().map(|id| &id.0)
71
114
    }
72

            
73
    /// Return true if this family has no members.
74
2278
    pub fn is_empty(&self) -> bool {
75
2278
        self.0.is_empty()
76
2278
    }
77
}
78

            
79
impl std::str::FromStr for RelayFamily {
80
    type Err = Error;
81
489206
    fn from_str(s: &str) -> Result<Self> {
82
489206
        let v: Result<Vec<LongIdent>> = s
83
489206
            .split(crate::parse::tokenize::is_sp)
84
824979
            .map(|e| e.parse::<LongIdent>())
85
489206
            .filter(Result::is_ok)
86
489206
            .collect();
87
489206
        Ok(RelayFamily(v?))
88
489206
    }
89
}
90

            
91
/// An identifier representing a relay family.
92
///
93
/// In the ["happy families"](https://spec.torproject.org/proposals/321) scheme,
94
/// microdescriptors will no longer have to contain a list of relay members,
95
/// but will instead contain these identifiers.
96
///
97
/// If two relays have a `RelayFamilyId` in common, they belong to the same family.
98
#[derive(Clone, Debug, Eq, PartialEq)]
99
#[non_exhaustive]
100
pub enum RelayFamilyId {
101
    /// An identifier derived from an Ed25519 relay family key. (`KP_familyid_ed`)
102
    Ed25519(Ed25519Identity),
103
    /// An unrecognized string.
104
    Unrecognized(String),
105
}
106

            
107
/// Prefix for a RelayFamilyId derived from an ed25519 `KP_familyid_ed`.
108
const ED25519_ID_PREFIX: &str = "ed25519:";
109

            
110
impl std::str::FromStr for RelayFamilyId {
111
    type Err = Error;
112

            
113
2178
    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
114
2178
        let mut buf = [0_u8; ED25519_ID_LEN];
115
2178
        if let Some(s) = s.strip_prefix(ED25519_ID_PREFIX) {
116
12
            if let Ok(decoded) = base64ct::Base64Unpadded::decode(s, &mut buf) {
117
12
                if let Some(ed_id) = Ed25519Identity::from_bytes(decoded) {
118
12
                    return Ok(RelayFamilyId::Ed25519(ed_id));
119
                }
120
            }
121
            return Err(NetdocErrorKind::BadArgument
122
                .with_msg("Invalid ed25519 family ID")
123
                .at_pos(Pos::at(s)));
124
2166
        }
125
2166
        Ok(RelayFamilyId::Unrecognized(s.to_string()))
126
2178
    }
127
}
128

            
129
impl std::fmt::Display for RelayFamilyId {
130
8
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131
8
        match self {
132
6
            RelayFamilyId::Ed25519(id) => write!(f, "{}{}", ED25519_ID_PREFIX, id),
133
2
            RelayFamilyId::Unrecognized(s) => write!(f, "{}", s),
134
        }
135
8
    }
136
}
137

            
138
impl PartialOrd for RelayFamilyId {
139
2
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
140
2
        Some(Ord::cmp(self, other))
141
2
    }
142
}
143
impl Ord for RelayFamilyId {
144
2
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
145
        // We sort RelayFamilyId values by string representation.
146
        // This is not super-efficient, but we don't need to do it very often.
147
2
        Ord::cmp(&self.to_string(), &other.to_string())
148
2
    }
149
}
150

            
151
impl NormalItemArgument for RelayFamilyId {}
152

            
153
/// A list of multiple [`RelayFamilyId`] entries as found in microdescs.
154
#[derive(Clone, Debug, Default, Eq, PartialEq, Deftly, derive_more::AsRef)]
155
#[derive_deftly(ItemValueParseable)]
156
pub struct RelayFamilyIds(
157
    // TODO DIRMIRROR: Replace with BTreeSet at one point.
158
    // TODO could/should this be a type alias instead?
159
    Vec<RelayFamilyId>,
160
);
161

            
162
impl RelayFamilyIds {
163
    /// Return a new empty [`RelayFamilyIds`].
164
477368
    pub fn new() -> Self {
165
477368
        Self::default()
166
477368
    }
167

            
168
    /// Push `family_id` onto this instance.
169
2160
    pub fn push(&mut self, family_id: RelayFamilyId) {
170
2160
        self.0.push(family_id);
171
2160
    }
172

            
173
    /// Sort entries ascending by their [`RelayFamilyId`].
174
    pub fn sort(&mut self) {
175
        self.0.sort();
176
    }
177

            
178
    /// Deduplicates entries.
179
    pub fn dedup(&mut self) {
180
        self.0.dedup();
181
    }
182
}
183

            
184
impl FromIterator<RelayFamilyId> for RelayFamilyIds {
185
458
    fn from_iter<T: IntoIterator<Item = RelayFamilyId>>(iter: T) -> Self {
186
458
        Self(iter.into_iter().collect())
187
458
    }
188
}
189

            
190
#[cfg(test)]
191
mod test {
192
    // @@ begin test lint list maintained by maint/add_warning @@
193
    #![allow(clippy::bool_assert_comparison)]
194
    #![allow(clippy::clone_on_copy)]
195
    #![allow(clippy::dbg_macro)]
196
    #![allow(clippy::mixed_attributes_style)]
197
    #![allow(clippy::print_stderr)]
198
    #![allow(clippy::print_stdout)]
199
    #![allow(clippy::single_char_pattern)]
200
    #![allow(clippy::unwrap_used)]
201
    #![allow(clippy::unchecked_time_subtraction)]
202
    #![allow(clippy::useless_vec)]
203
    #![allow(clippy::needless_pass_by_value)]
204
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
205
    use std::str::FromStr;
206

            
207
    use super::*;
208
    use crate::Result;
209
    #[test]
210
    fn family() -> Result<()> {
211
        let f = "nickname1 nickname2 $ffffffffffffffffffffffffffffffffffffffff=foo eeeeeeeeeeeeeeeeeeeEEEeeeeeeeeeeeeeeeeee ddddddddddddddddddddddddddddddddd  $cccccccccccccccccccccccccccccccccccccccc~blarg ".parse::<RelayFamily>()?;
212
        let v = vec![
213
            RsaIdentity::from_bytes(
214
                &hex::decode("ffffffffffffffffffffffffffffffffffffffff").unwrap()[..],
215
            )
216
            .unwrap(),
217
            RsaIdentity::from_bytes(
218
                &hex::decode("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap()[..],
219
            )
220
            .unwrap(),
221
            RsaIdentity::from_bytes(
222
                &hex::decode("cccccccccccccccccccccccccccccccccccccccc").unwrap()[..],
223
            )
224
            .unwrap(),
225
        ];
226
        assert_eq!(f.members().cloned().collect::<Vec<_>>(), v);
227
        Ok(())
228
    }
229

            
230
    #[test]
231
    fn test_contains() -> Result<()> {
232
        let family =
233
            "ffffffffffffffffffffffffffffffffffffffff eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
234
                .parse::<RelayFamily>()?;
235
        let in_family = RsaIdentity::from_bytes(
236
            &hex::decode("ffffffffffffffffffffffffffffffffffffffff").unwrap()[..],
237
        )
238
        .unwrap();
239
        let not_in_family = RsaIdentity::from_bytes(
240
            &hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()[..],
241
        )
242
        .unwrap();
243
        assert!(family.contains(&in_family), "Relay not found in family");
244
        assert!(
245
            !family.contains(&not_in_family),
246
            "Extra relay found in family"
247
        );
248
        Ok(())
249
    }
250

            
251
    #[test]
252
    fn mutable() {
253
        let mut family = RelayFamily::default();
254
        let key = RsaIdentity::from_hex("ffffffffffffffffffffffffffffffffffffffff").unwrap();
255
        assert!(!family.contains(&key));
256
        family.push(key);
257
        assert!(family.contains(&key));
258
    }
259

            
260
    #[test]
261
    fn family_ids() {
262
        let ed_str_rep = "ed25519:7sToQRuge1bU2hS0CG0ViMndc4m82JhO4B4kdrQey80";
263
        let ed_id = RelayFamilyId::from_str(ed_str_rep).unwrap();
264
        assert!(matches!(ed_id, RelayFamilyId::Ed25519(_)));
265
        assert_eq!(ed_id.to_string().as_str(), ed_str_rep);
266

            
267
        let other_str_rep = "hello-world";
268
        let other_id = RelayFamilyId::from_str(other_str_rep).unwrap();
269
        assert!(matches!(other_id, RelayFamilyId::Unrecognized(_)));
270
        assert_eq!(other_id.to_string().as_str(), other_str_rep);
271

            
272
        assert_eq!(ed_id, ed_id);
273
        assert_ne!(ed_id, other_id);
274
    }
275

            
276
    #[test]
277
    fn parse2() {
278
        #[derive(Debug, PartialEq, Eq, derive_deftly::Deftly)]
279
        #[derive_deftly(NetdocParseable)]
280
        struct Wrapper {
281
            family: RelayFamily,
282
        }
283

            
284
        const LINE: &str = "family $0000000000000000000000000000000000000000 $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
285
        let parsed =
286
            crate::parse2::parse_netdoc::<Wrapper>(&crate::parse2::ParseInput::new(LINE, ""))
287
                .unwrap();
288
        assert_eq!(
289
            parsed,
290
            Wrapper {
291
                family: RelayFamily(vec![
292
                    RsaIdentity::from_hex("0000000000000000000000000000000000000000")
293
                        .unwrap()
294
                        .into(),
295
                    RsaIdentity::from_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
296
                        .unwrap()
297
                        .into()
298
                ])
299
            }
300
        );
301
    }
302
}