tor_netdoc/doc/microdesc/
build.rs1use super::Microdesc;
7
8use crate::types::family::{RelayFamily, RelayFamilyId};
9use crate::types::policy::PortPolicy;
10use crate::{BuildError as Error, BuildResult as Result, Error as ParseError};
11use tor_llcrypto::pk::{curve25519, ed25519};
12
13use rand::Rng;
14
15#[cfg_attr(docsrs, doc(cfg(feature = "build_docs")))]
22#[derive(Debug, Clone)]
23pub struct MicrodescBuilder {
24 ntor_onion_key: Option<curve25519::PublicKey>,
28 family: RelayFamily,
32 family_ids: Vec<RelayFamilyId>,
34 ipv4_policy: PortPolicy,
36 ipv6_policy: PortPolicy,
38 ed25519_id: Option<ed25519::Ed25519Identity>,
40}
41
42impl MicrodescBuilder {
43 pub(crate) fn new() -> Self {
45 MicrodescBuilder {
46 ntor_onion_key: None,
47 family: RelayFamily::new(),
48 family_ids: Vec::new(),
49 ipv4_policy: PortPolicy::new_reject_all(),
50 ipv6_policy: PortPolicy::new_reject_all(),
51 ed25519_id: None,
52 }
53 }
54
55 pub fn ntor_key(&mut self, key: curve25519::PublicKey) -> &mut Self {
59 self.ntor_onion_key = Some(key);
60 self
61 }
62
63 pub fn ed25519_id(&mut self, key: ed25519::Ed25519Identity) -> &mut Self {
67 self.ed25519_id = Some(key);
68 self
69 }
70
71 pub fn family(&mut self, family: RelayFamily) -> &mut Self {
75 self.family = family;
76 self
77 }
78
79 pub fn add_family_id(&mut self, id: RelayFamilyId) -> &mut Self {
81 self.family_ids.push(id);
82 self
83 }
84
85 pub fn ipv4_policy(&mut self, policy: PortPolicy) -> &mut Self {
89 self.ipv4_policy = policy;
90 self
91 }
92
93 pub fn ipv6_policy(&mut self, policy: PortPolicy) -> &mut Self {
97 self.ipv6_policy = policy;
98 self
99 }
100
101 pub fn parse_family(&mut self, family: &str) -> Result<&mut Self> {
103 Ok(self.family(family.parse()?))
104 }
105
106 pub fn parse_ipv4_policy(&mut self, policy: &str) -> Result<&mut Self> {
111 Ok(self.ipv4_policy(policy.parse().map_err(ParseError::from)?))
112 }
113
114 pub fn parse_ipv6_policy(&mut self, policy: &str) -> Result<&mut Self> {
119 Ok(self.ipv6_policy(policy.parse().map_err(ParseError::from)?))
120 }
121
122 pub fn testing_md(&self) -> Result<Microdesc> {
135 let ntor_onion_key = self
136 .ntor_onion_key
137 .ok_or(Error::CannotBuild("Missing ntor_key"))?;
138 let ed25519_id = self
139 .ed25519_id
140 .ok_or(Error::CannotBuild("Missing ed25519_id"))?;
141
142 let sha256 = rand::rng().random();
145
146 Ok(Microdesc {
147 sha256,
148 ntor_onion_key,
149 family: self.family.clone().intern(),
150 family_ids: self.family_ids.clone(),
151 ipv4_policy: self.ipv4_policy.clone().intern(),
152 ipv6_policy: self.ipv6_policy.clone().intern(),
153 ed25519_id,
154 })
155 }
156}
157
158#[cfg(test)]
159mod test {
160 #![allow(clippy::bool_assert_comparison)]
162 #![allow(clippy::clone_on_copy)]
163 #![allow(clippy::dbg_macro)]
164 #![allow(clippy::mixed_attributes_style)]
165 #![allow(clippy::print_stderr)]
166 #![allow(clippy::print_stdout)]
167 #![allow(clippy::single_char_pattern)]
168 #![allow(clippy::unwrap_used)]
169 #![allow(clippy::unchecked_duration_subtraction)]
170 #![allow(clippy::useless_vec)]
171 #![allow(clippy::needless_pass_by_value)]
172 use super::*;
174
175 #[test]
176 fn minimal() {
177 let ed: ed25519::Ed25519Identity = (*b"this is not much of a public key").into();
178 let ntor: curve25519::PublicKey = (*b"but fortunately nothing cares...").into();
179
180 let md = MicrodescBuilder::new()
181 .ed25519_id(ed)
182 .ntor_key(ntor)
183 .testing_md()
184 .unwrap();
185
186 assert_eq!(md.ed25519_id(), &ed);
187 assert_eq!(md.ntor_key(), &ntor);
188
189 assert_eq!(md.family().members().count(), 0);
190 }
191
192 #[test]
193 fn maximal() -> Result<()> {
194 let ed: ed25519::Ed25519Identity = (*b"this is not much of a public key").into();
195 let ntor: curve25519::PublicKey = (*b"but fortunately nothing cares...").into();
196
197 let md = Microdesc::builder()
198 .ed25519_id(ed)
199 .ntor_key(ntor)
200 .parse_ipv4_policy("accept 80,443")?
201 .parse_ipv6_policy("accept 22-80")?
202 .parse_family("$aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa $bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")?
203 .testing_md()
204 .unwrap();
205
206 assert_eq!(md.family().members().count(), 2);
207 assert!(md.family().contains(&[0xaa; 20].into()));
208
209 assert!(md.ipv4_policy().allows_port(443));
210 assert!(md.ipv4_policy().allows_port(80));
211 assert!(!md.ipv4_policy().allows_port(55));
212
213 assert!(!md.ipv6_policy().allows_port(443));
214 assert!(md.ipv6_policy().allows_port(80));
215 assert!(md.ipv6_policy().allows_port(55));
216
217 Ok(())
218 }
219
220 #[test]
221 fn failing() {
222 let ed: ed25519::Ed25519Identity = (*b"this is not much of a public key").into();
223 let ntor: curve25519::PublicKey = (*b"but fortunately nothing cares...").into();
224
225 {
226 let mut builder = Microdesc::builder();
227 builder.ed25519_id(ed);
228 assert!(builder.testing_md().is_err()); }
230
231 {
232 let mut builder = Microdesc::builder();
233 builder.ntor_key(ntor);
234 assert!(builder.testing_md().is_err()); }
236 }
237}