tor_netdoc/parse2/poc/
netstatus.rs1use super::*;
4
5use crate::types;
6use authcert::DirAuthKeyCert;
7
8mod ns_per_flavour_macros;
9pub use ns_per_flavour_macros::*;
10
11ns_per_flavour_macros::ns_export_flavoured_types! {
12 NetworkStatus, NetworkStatusSigned, Router,
13}
14
15#[derive(Debug, Clone, Copy, Eq, PartialEq, strum::EnumString, strum::Display)]
17#[non_exhaustive]
18pub enum NdaNetworkStatusVersion {
19 #[strum(serialize = "3")]
21 V3,
22}
23
24impl NormalItemArgument for NdaNetworkStatusVersion {}
25
26#[derive(Clone, Debug, Default, Deftly)]
28#[derive_deftly(ItemValueParseable)]
29#[non_exhaustive]
30pub struct NdiParams {
31 }
33
34#[derive(Deftly, Clone, Debug)]
36#[derive_deftly(ItemValueParseable)]
37#[non_exhaustive]
38pub struct NdiR {
39 pub nickname: types::Nickname,
41 pub identity: String, }
44
45#[derive(Debug, Clone)]
47#[non_exhaustive]
48pub enum NdiDirectorySignature {
49 Known {
51 h_kp_auth_id_rsa: pk::rsa::RsaIdentity,
53 h_kp_auth_sign_rsa: pk::rsa::RsaIdentity,
55 rsa_signature: Vec<u8>,
57 hash: DirectorySignatureHash,
59 },
60 Unknown {},
65}
66define_derive_deftly! {
67 DirectorySignatureHash expect items, beta_deftly:
69
70 impl $ttype {
71 fn parse_keyword_and_hash(algorithm: &str, body: &SignatureHashInputs) -> Option<Self> {
73 Some(match algorithm {
74 $(
75 ${concat ${kebab_case $vname}} => {
76 let mut h = tor_llcrypto::d::$vname::new();
77 h.update(body.body().body());
78 h.update(body.signature_item_kw_spc);
79 Self::$vname(h.finalize().into())
80 }
81 )
82 _ => return None,
83 })
84 }
85
86 fn hash_slice_for_verification(&self) -> &[u8] {
87 match self { $(
88 $vpat => f_0,
89 ) }
90 }
91 }
92}
93
94#[derive(Clone, Copy, Debug, Eq, PartialEq, strum::EnumString, Deftly)]
96#[derive_deftly(DirectorySignatureHash)]
97#[non_exhaustive]
98pub enum DirectorySignatureHash {
99 Sha1([u8; 20]),
101 Sha256([u8; 32]),
103}
104
105#[derive(Clone, Debug, Error)]
109#[non_exhaustive]
110#[error("invalid value for vote-status in network status document")]
111pub struct InvalidNetworkStatusVoteStatus {}
112
113impl SignatureItemParseable for NdiDirectorySignature {
114 fn from_unparsed_and_body<'s>(
116 mut input: UnparsedItem<'s>,
117 document_body: &SignatureHashInputs<'_>,
118 ) -> Result<Self, EP> {
119 let object = input.object();
120 let args = input.args_mut();
121 let maybe_algorithm = args.clone().next().ok_or(EP::MissingArgument {
122 field: "algorithm/h_kp_auth_id_rsa",
123 })?;
124
125 let hash = if let Some(hash) =
126 DirectorySignatureHash::parse_keyword_and_hash(maybe_algorithm, document_body)
127 {
128 let _: &str = args.next().expect("we just peeked");
129 hash
130 } else if maybe_algorithm
131 .find(|c: char| !c.is_ascii_hexdigit())
132 .is_some()
133 {
134 return Ok(NdiDirectorySignature::Unknown {});
137 } else {
138 DirectorySignatureHash::parse_keyword_and_hash("sha1", document_body)
139 .expect("sha1 is not valid?")
140 };
141
142 let rsa_signature = object.ok_or(EP::MissingObject)?.decode_data()?;
143
144 let mut fingerprint_arg = |field: &'static str| {
145 (|| {
146 args.next()
147 .ok_or(AE::Missing)?
148 .parse::<types::Fingerprint>()
149 .map_err(|_e| AE::Invalid)
150 .map(pk::rsa::RsaIdentity::from)
151 })()
152 .map_err(args.error_handler(field))
153 };
154
155 Ok(NdiDirectorySignature::Known {
156 rsa_signature,
157 h_kp_auth_id_rsa: fingerprint_arg("h_kp_auth_id_rsa")?,
158 h_kp_auth_sign_rsa: fingerprint_arg("h_kp_auth_sign_rsa")?,
159 hash,
160 })
161 }
162}
163
164fn verify_general_timeless(
172 signatures: &[NdiDirectorySignature],
173 trusted: &[pk::rsa::RsaIdentity],
174 certs: &[&DirAuthKeyCert],
175 threshold: usize,
176) -> Result<(), VF> {
177 let mut ok = HashSet::<pk::rsa::RsaIdentity>::new();
178
179 for sig in signatures {
180 match sig {
181 NdiDirectorySignature::Known {
182 hash,
183 h_kp_auth_id_rsa,
184 h_kp_auth_sign_rsa,
185 rsa_signature,
186 } => {
187 let Some(authority) = ({
188 trusted
189 .iter()
190 .find(|trusted| **trusted == *h_kp_auth_id_rsa)
191 }) else {
192 continue;
194 };
195 let Some(cert) = ({
196 certs
197 .iter()
198 .find(|cert| cert.kp_auth_sign_rsa.to_rsa_identity() == *h_kp_auth_sign_rsa)
199 }) else {
200 continue;
202 };
203
204 let h = hash.hash_slice_for_verification();
205
206 let () = cert.kp_auth_sign_rsa.verify(h, rsa_signature)?;
207
208 ok.insert(*authority);
209 }
210 NdiDirectorySignature::Unknown { .. } => {}
211 }
212 }
213
214 if ok.len() < threshold {
215 return Err(VF::InsufficientTrustedSigners);
216 }
217
218 Ok(())
219}