tor_netdoc/parse2/poc/
netstatus.rs1use super::*;
4
5use crate::doc::{self, authcert};
6use crate::types;
7use authcert::AuthCert as DirAuthKeyCert;
8use doc::netstatus::{ConsensusAuthoritySection, VoteAuthoritySection};
9
10mod ns_per_flavour_macros;
11pub use ns_per_flavour_macros::*;
12
13ns_per_flavour_macros::ns_export_flavoured_types! {
14 NetworkStatus, NetworkStatusUnverified, Router,
15}
16
17#[derive(Debug, Clone, Copy, Eq, PartialEq, strum::EnumString, strum::Display)]
19#[non_exhaustive]
20pub enum NdaNetworkStatusVersion {
21 #[strum(serialize = "3")]
23 V3,
24}
25
26impl NormalItemArgument for NdaNetworkStatusVersion {}
27
28#[derive(Clone, Debug, Default, Deftly)]
30#[derive_deftly(ItemValueParseable)]
31#[non_exhaustive]
32pub struct NdiParams {
33 }
35
36#[derive(Deftly, Clone, Debug)]
38#[derive_deftly(ItemValueParseable)]
39#[non_exhaustive]
40pub struct NdiR {
41 pub nickname: types::Nickname,
43 pub identity: String, }
46
47#[derive(Debug, Clone)]
49#[non_exhaustive]
50pub enum NdiDirectorySignature {
51 Known {
53 hash_algo: DirectorySignatureHashAlgo,
55 h_kp_auth_id_rsa: pk::rsa::RsaIdentity,
57 h_kp_auth_sign_rsa: pk::rsa::RsaIdentity,
59 rsa_signature: Vec<u8>,
61 },
62 Unknown {},
67}
68define_derive_deftly! {
69 DirectorySignatureHashesAccu expect items, beta_deftly:
88
89 ${define FNAME ${paste ${snake_case $vname}} }
90
91 #[derive(Clone, Copy, Default, Debug, Eq, PartialEq, Deftly)]
93 #[derive_deftly(AsMutSelf)]
94 #[non_exhaustive]
95 pub struct DirectorySignaturesHashesAccu {
96 $(
97 ${vattrs doc}
98 $FNAME: Option<[u8; ${vmeta(hash_len) as expr}]>,
99 )
100 }
101
102 impl DirectorySignaturesHashesAccu {
103 fn parse_keyword_and_hash(
107 &mut self,
108 algorithm: &str,
109 body: &SignatureHashInputs,
110 ) -> Option<$ttype> {
111 Some(match algorithm {
112 $(
113 ${concat $FNAME} => {
114 let mut h = tor_llcrypto::d::$vname::new();
115 h.update(body.body().body());
116 h.update(body.signature_item_kw_spc);
117 self.$FNAME = Some(h.finalize().into());
118 $vtype
119 }
120 )
121 _ => return None,
122 })
123 }
124
125 fn hash_slice_for_verification(&self, algo: $ttype) -> Option<&[u8]> {
130 match algo { $(
131 $vtype => Some(self.$FNAME.as_ref()?),
132 ) }
133 }
134 }
135}
136
137define_directory_signature_hash_algo! {
138 #[derive_deftly(DirectorySignatureHashesAccu)]
139}
140
141#[derive(Clone, Debug, Error)]
145#[non_exhaustive]
146#[error("invalid value for vote-status in network status document")]
147pub struct InvalidNetworkStatusVoteStatus {}
148
149impl SignatureItemParseable for NdiDirectorySignature {
150 type HashAccu = DirectorySignaturesHashesAccu;
151
152 fn from_unparsed_and_body<'s>(
154 mut input: UnparsedItem<'s>,
155 document_body: &SignatureHashInputs<'_>,
156 hashes: &mut DirectorySignaturesHashesAccu,
157 ) -> Result<Self, EP> {
158 let object = input.object();
159 let args = input.args_mut();
160 let maybe_algorithm = args.clone().next().ok_or(EP::MissingArgument {
161 field: "algorithm/h_kp_auth_id_rsa",
162 })?;
163
164 let hash_algo =
165 if let Some(algo) = hashes.parse_keyword_and_hash(maybe_algorithm, document_body) {
166 let _: &str = args.next().expect("we just peeked");
167 algo
168 } else if maybe_algorithm
169 .find(|c: char| !c.is_ascii_hexdigit())
170 .is_some()
171 {
172 return Ok(NdiDirectorySignature::Unknown {});
175 } else {
176 hashes
177 .parse_keyword_and_hash("sha1", document_body)
178 .expect("sha1 is not valid?")
179 };
180
181 let rsa_signature = object.ok_or(EP::MissingObject)?.decode_data()?;
182
183 let mut fingerprint_arg = |field: &'static str| {
184 (|| {
185 args.next()
186 .ok_or(AE::Missing)?
187 .parse::<types::Fingerprint>()
188 .map_err(|_e| AE::Invalid)
189 .map(pk::rsa::RsaIdentity::from)
190 })()
191 .map_err(args.error_handler(field))
192 };
193
194 Ok(NdiDirectorySignature::Known {
195 hash_algo,
196 rsa_signature,
197 h_kp_auth_id_rsa: fingerprint_arg("h_kp_auth_id_rsa")?,
198 h_kp_auth_sign_rsa: fingerprint_arg("h_kp_auth_sign_rsa")?,
199 })
200 }
201}
202
203fn verify_general_timeless(
211 hashes: &DirectorySignaturesHashesAccu,
212 signatures: &[NdiDirectorySignature],
213 trusted: &[pk::rsa::RsaIdentity],
214 certs: &[&DirAuthKeyCert],
215 threshold: usize,
216) -> Result<(), VF> {
217 let mut ok = HashSet::<pk::rsa::RsaIdentity>::new();
218
219 for sig in signatures {
220 match sig {
221 NdiDirectorySignature::Known {
222 hash_algo,
223 h_kp_auth_id_rsa,
224 h_kp_auth_sign_rsa,
225 rsa_signature,
226 } => {
227 let Some(authority) = ({
228 trusted
229 .iter()
230 .find(|trusted| **trusted == *h_kp_auth_id_rsa)
231 }) else {
232 continue;
234 };
235 let Some(cert) = ({
236 certs
237 .iter()
238 .find(|cert| cert.dir_signing_key.to_rsa_identity() == *h_kp_auth_sign_rsa)
239 }) else {
240 continue;
242 };
243
244 let h = hashes
245 .hash_slice_for_verification(*hash_algo)
246 .ok_or(VF::Bug)?;
247
248 let () = cert.dir_signing_key.verify(h, rsa_signature)?;
249
250 ok.insert(*authority);
251 }
252 NdiDirectorySignature::Unknown { .. } => {}
253 }
254 }
255
256 if ok.len() < threshold {
257 return Err(VF::InsufficientTrustedSigners);
258 }
259
260 Ok(())
261}