tor_netdoc/types/
family.rs1use std::sync::Arc;
7
8use crate::types::misc::LongIdent;
9use crate::util::intern::InternCache;
10use crate::{Error, NetdocErrorKind, Pos, Result};
11use base64ct::Encoding;
12use tor_llcrypto::pk::ed25519::{Ed25519Identity, ED25519_ID_LEN};
13use tor_llcrypto::pk::rsa::RsaIdentity;
14
15#[derive(Clone, Debug, Default, Hash, Eq, PartialEq)]
28pub struct RelayFamily(Vec<RsaIdentity>);
29
30static FAMILY_CACHE: InternCache<RelayFamily> = InternCache::new();
35
36impl RelayFamily {
37 pub fn new() -> Self {
39 RelayFamily::default()
40 }
41
42 pub fn push(&mut self, rsa_id: RsaIdentity) {
44 self.0.push(rsa_id);
45 }
46
47 fn normalize(&mut self) {
49 self.0.sort();
50 self.0.dedup();
51 }
52
53 pub fn intern(mut self) -> Arc<Self> {
56 self.normalize();
57 FAMILY_CACHE.intern(self)
58 }
59
60 pub fn contains(&self, rsa_id: &RsaIdentity) -> bool {
62 self.0.contains(rsa_id)
63 }
64
65 pub fn members(&self) -> impl Iterator<Item = &RsaIdentity> {
68 self.0.iter()
69 }
70
71 pub fn is_empty(&self) -> bool {
73 self.0.is_empty()
74 }
75}
76
77impl std::str::FromStr for RelayFamily {
78 type Err = Error;
79 fn from_str(s: &str) -> Result<Self> {
80 let v: Result<Vec<RsaIdentity>> = s
81 .split(crate::parse::tokenize::is_sp)
82 .map(|e| e.parse::<LongIdent>().map(|v| v.into()))
83 .filter(Result::is_ok)
84 .collect();
85 Ok(RelayFamily(v?))
86 }
87}
88
89#[derive(Clone, Debug, Eq, PartialEq)]
97#[non_exhaustive]
98pub enum RelayFamilyId {
99 Ed25519(Ed25519Identity),
101 Unrecognized(String),
103}
104
105const ED25519_ID_PREFIX: &str = "ed25519:";
107
108impl std::str::FromStr for RelayFamilyId {
109 type Err = Error;
110
111 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
112 let mut buf = [0_u8; ED25519_ID_LEN];
113 if let Some(s) = s.strip_prefix(ED25519_ID_PREFIX) {
114 if let Ok(decoded) = base64ct::Base64Unpadded::decode(s, &mut buf) {
115 if let Some(ed_id) = Ed25519Identity::from_bytes(decoded) {
116 return Ok(RelayFamilyId::Ed25519(ed_id));
117 }
118 }
119 return Err(NetdocErrorKind::BadArgument
120 .with_msg("Invalid ed25519 family ID")
121 .at_pos(Pos::at(s)));
122 }
123 Ok(RelayFamilyId::Unrecognized(s.to_string()))
124 }
125}
126
127impl std::fmt::Display for RelayFamilyId {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 match self {
130 RelayFamilyId::Ed25519(id) => write!(f, "{}{}", ED25519_ID_PREFIX, id),
131 RelayFamilyId::Unrecognized(s) => write!(f, "{}", s),
132 }
133 }
134}
135
136impl PartialOrd for RelayFamilyId {
137 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
138 Some(Ord::cmp(self, other))
139 }
140}
141impl Ord for RelayFamilyId {
142 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
143 Ord::cmp(&self.to_string(), &other.to_string())
146 }
147}
148
149#[cfg(test)]
150mod test {
151 #![allow(clippy::bool_assert_comparison)]
153 #![allow(clippy::clone_on_copy)]
154 #![allow(clippy::dbg_macro)]
155 #![allow(clippy::mixed_attributes_style)]
156 #![allow(clippy::print_stderr)]
157 #![allow(clippy::print_stdout)]
158 #![allow(clippy::single_char_pattern)]
159 #![allow(clippy::unwrap_used)]
160 #![allow(clippy::unchecked_duration_subtraction)]
161 #![allow(clippy::useless_vec)]
162 #![allow(clippy::needless_pass_by_value)]
163 use std::str::FromStr;
165
166 use super::*;
167 use crate::Result;
168 #[test]
169 fn family() -> Result<()> {
170 let f = "nickname1 nickname2 $ffffffffffffffffffffffffffffffffffffffff=foo eeeeeeeeeeeeeeeeeeeEEEeeeeeeeeeeeeeeeeee ddddddddddddddddddddddddddddddddd $cccccccccccccccccccccccccccccccccccccccc~blarg ".parse::<RelayFamily>()?;
171 let v = vec![
172 RsaIdentity::from_bytes(
173 &hex::decode("ffffffffffffffffffffffffffffffffffffffff").unwrap()[..],
174 )
175 .unwrap(),
176 RsaIdentity::from_bytes(
177 &hex::decode("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap()[..],
178 )
179 .unwrap(),
180 RsaIdentity::from_bytes(
181 &hex::decode("cccccccccccccccccccccccccccccccccccccccc").unwrap()[..],
182 )
183 .unwrap(),
184 ];
185 assert_eq!(f.0, v);
186 Ok(())
187 }
188
189 #[test]
190 fn test_contains() -> Result<()> {
191 let family =
192 "ffffffffffffffffffffffffffffffffffffffff eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
193 .parse::<RelayFamily>()?;
194 let in_family = RsaIdentity::from_bytes(
195 &hex::decode("ffffffffffffffffffffffffffffffffffffffff").unwrap()[..],
196 )
197 .unwrap();
198 let not_in_family = RsaIdentity::from_bytes(
199 &hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()[..],
200 )
201 .unwrap();
202 assert!(family.contains(&in_family), "Relay not found in family");
203 assert!(
204 !family.contains(¬_in_family),
205 "Extra relay found in family"
206 );
207 Ok(())
208 }
209
210 #[test]
211 fn mutable() {
212 let mut family = RelayFamily::default();
213 let key = RsaIdentity::from_hex("ffffffffffffffffffffffffffffffffffffffff").unwrap();
214 assert!(!family.contains(&key));
215 family.push(key);
216 assert!(family.contains(&key));
217 }
218
219 #[test]
220 fn family_ids() {
221 let ed_str_rep = "ed25519:7sToQRuge1bU2hS0CG0ViMndc4m82JhO4B4kdrQey80";
222 let ed_id = RelayFamilyId::from_str(ed_str_rep).unwrap();
223 assert!(matches!(ed_id, RelayFamilyId::Ed25519(_)));
224 assert_eq!(ed_id.to_string().as_str(), ed_str_rep);
225
226 let other_str_rep = "hello-world";
227 let other_id = RelayFamilyId::from_str(other_str_rep).unwrap();
228 assert!(matches!(other_id, RelayFamilyId::Unrecognized(_)));
229 assert_eq!(other_id.to_string().as_str(), other_str_rep);
230
231 assert_eq!(ed_id, ed_id);
232 assert_ne!(ed_id, other_id);
233 }
234}