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)]
17#[non_exhaustive]
18pub enum NdaNetworkStatusVersion {
19 #[strum(serialize = "3")]
21 V3,
22}
23
24#[derive(Clone, Debug, Default, Deftly)]
26#[derive_deftly(ItemValueParseable)]
27#[non_exhaustive]
28pub struct NdiParams {
29 }
31
32#[derive(Deftly, Clone, Debug)]
34#[derive_deftly(ItemValueParseable)]
35#[non_exhaustive]
36pub struct NdiR {
37 pub nickname: types::Nickname,
39 pub identity: String, }
42
43#[derive(Debug, Clone)]
45#[non_exhaustive]
46pub enum NdiDirectorySignature {
47 Known {
49 h_kp_auth_id_rsa: pk::rsa::RsaIdentity,
51 h_kp_auth_sign_rsa: pk::rsa::RsaIdentity,
53 rsa_signature: Vec<u8>,
55 hash: DirectorySignatureHash,
57 },
58 Unknown {},
63}
64define_derive_deftly! {
65 DirectorySignatureHash expect items, beta_deftly:
67
68 impl $ttype {
69 fn parse_keyword_and_hash(algorithm: &str, body: &SignatureHashInputs) -> Option<Self> {
71 Some(match algorithm {
72 $(
73 ${concat ${kebab_case $vname}} => {
74 let mut h = tor_llcrypto::d::$vname::new();
75 h.update(body.body().body());
76 h.update(body.signature_item_kw_spc);
77 Self::$vname(h.finalize().into())
78 }
79 )
80 _ => return None,
81 })
82 }
83
84 fn hash_slice_for_verification(&self) -> &[u8] {
85 match self { $(
86 $vpat => f_0,
87 ) }
88 }
89 }
90}
91
92#[derive(Clone, Copy, Debug, Eq, PartialEq, strum::EnumString, Deftly)]
94#[derive_deftly(DirectorySignatureHash)]
95#[non_exhaustive]
96pub enum DirectorySignatureHash {
97 Sha1([u8; 20]),
99 Sha256([u8; 32]),
101}
102
103#[derive(Clone, Debug, Error)]
107#[non_exhaustive]
108#[error("invalid value for vote-status in network status document")]
109pub struct InvalidNetworkStatusVoteStatus {}
110
111impl SignatureItemParseable for NdiDirectorySignature {
112 fn from_unparsed_and_body<'s>(
114 mut input: UnparsedItem<'s>,
115 document_body: &SignatureHashInputs<'_>,
116 ) -> Result<Self, EP> {
117 let object = input.object();
118 let args = input.args_mut();
119 let maybe_algorithm = args.clone().next().ok_or(EP::MissingArgument {
120 field: "algorithm/h_kp_auth_id_rsa",
121 })?;
122
123 let hash = if let Some(hash) =
124 DirectorySignatureHash::parse_keyword_and_hash(maybe_algorithm, document_body)
125 {
126 let _: &str = args.next().expect("we just peeked");
127 hash
128 } else if maybe_algorithm
129 .find(|c: char| !c.is_ascii_hexdigit())
130 .is_some()
131 {
132 return Ok(NdiDirectorySignature::Unknown {});
135 } else {
136 DirectorySignatureHash::parse_keyword_and_hash("sha1", document_body)
137 .expect("sha1 is not valid?")
138 };
139
140 let rsa_signature = object.ok_or(EP::MissingObject)?.decode_data()?;
141
142 let mut fingerprint_arg = |field: &'static str| {
143 args.next()
144 .ok_or(EP::MissingArgument { field })?
145 .parse::<types::Fingerprint>()
146 .map_err(|_e| EP::InvalidArgument { field })
147 .map(pk::rsa::RsaIdentity::from)
148 };
149
150 Ok(NdiDirectorySignature::Known {
151 rsa_signature,
152 h_kp_auth_id_rsa: fingerprint_arg("h_kp_auth_id_rsa")?,
153 h_kp_auth_sign_rsa: fingerprint_arg("h_kp_auth_sign_rsa")?,
154 hash,
155 })
156 }
157}
158
159fn verify_general_timeless(
167 signatures: &[NdiDirectorySignature],
168 trusted: &[pk::rsa::RsaIdentity],
169 certs: &[&DirAuthKeyCert],
170 threshold: usize,
171) -> Result<(), VF> {
172 let mut ok = HashSet::<pk::rsa::RsaIdentity>::new();
173
174 for sig in signatures {
175 match sig {
176 NdiDirectorySignature::Known {
177 hash,
178 h_kp_auth_id_rsa,
179 h_kp_auth_sign_rsa,
180 rsa_signature,
181 } => {
182 let Some(authority) = ({
183 trusted
184 .iter()
185 .find(|trusted| **trusted == *h_kp_auth_id_rsa)
186 }) else {
187 continue;
189 };
190 let Some(cert) = ({
191 certs
192 .iter()
193 .find(|cert| cert.kp_auth_sign_rsa.to_rsa_identity() == *h_kp_auth_sign_rsa)
194 }) else {
195 continue;
197 };
198
199 let h = hash.hash_slice_for_verification();
200
201 let () = cert.kp_auth_sign_rsa.verify(h, rsa_signature)?;
202
203 ok.insert(*authority);
204 }
205 NdiDirectorySignature::Unknown { .. } => {}
206 }
207 }
208
209 if ok.len() < threshold {
210 return Err(VF::InsufficientTrustedSigners);
211 }
212
213 Ok(())
214}