Skip to main content

tor_netdoc/parse2/poc/netstatus/
flavoured.rs

1//! network status documents - types that vary by flavour
2//!
3//! **This file is reincluded multiple times**,
4//! once for each consensus flavour, and once for votes.
5//!
6//! Each time, with different behaviour for the macros `ns_***`.
7//!
8//! Thus, this file generates (for example) all three of:
9//! `ns::NetworkStatus` aka `NetworkStatusNs`,
10//! `NetworkStatusMd` and `NetworkStatusVote`.
11//!
12//! (We treat votes as a "flavour".)
13
14use super::super::*;
15
16/// Toplevel document string for error reporting
17const TOPLEVEL_DOCTYPE_FOR_ERROR: &str =
18    ns_expr!("NetworkStatusVote", "NetworkStatusNs", "NetworkStatusMd",);
19
20/// The real router status entry type.
21pub type Router = ns_type!(
22    crate::doc::netstatus::VoteRouterStatus,
23    crate::doc::netstatus::PlainRouterStatus,
24    crate::doc::netstatus::MdRouterStatus,
25);
26
27/// The real footer type.
28pub type NddDirectoryFooter = ns_type!(
29    crate::doc::netstatus::VoteFooter,
30    crate::doc::netstatus::PlainFooter,
31    crate::doc::netstatus::MdFooter,
32);
33
34/// The real signatures section type.
35pub type NetworkStatusSignatures = ns_type!(
36    crate::doc::netstatus::vote::NetworkStatusSignatures,
37    crate::doc::netstatus::plain::NetworkStatusSignatures,
38    crate::doc::netstatus::md::NetworkStatusSignatures,
39);
40
41/// The real `network-status-version` item type.
42pub type NetworkStatusVersionItem = ns_type!(
43    crate::doc::netstatus::vote::NetworkStatusVersionItem,
44    crate::doc::netstatus::plain::NetworkStatusVersionItem,
45    crate::doc::netstatus::md::NetworkStatusVersionItem,
46);
47
48/// Network status document (vote, consensus, or microdescriptor consensus) - body
49///
50/// The preamble items are members of this struct.
51/// The rest are handled as sub-documents.
52#[derive(Deftly, Clone, Debug)]
53#[derive_deftly(NetdocParseableUnverified)]
54#[deftly(netdoc(doctype_for_error = TOPLEVEL_DOCTYPE_FOR_ERROR))]
55#[non_exhaustive]
56pub struct NetworkStatus {
57    /// `network-status-version`
58    pub network_status_version: NetworkStatusVersionItem,
59
60    /// `vote-status`
61    pub vote_status: NdiVoteStatus,
62
63    /// `published`
64    pub published: ns_type!((NdaSystemTimeDeprecatedSyntax,), Option<Void>,),
65
66    /// `valid-after`
67    pub valid_after: (NdaSystemTimeDeprecatedSyntax,),
68
69    /// `valid-until`
70    pub valid_until: (NdaSystemTimeDeprecatedSyntax,),
71
72    /// `voting-delay`
73    pub voting_delay: NdiVotingDelay,
74
75    /// `params`
76    #[deftly(netdoc(default))]
77    pub params: NdiParams,
78
79    /// Authority section
80    #[deftly(netdoc(subdoc))]
81    pub authority: NddAuthoritySection,
82
83    /// `r` subdocuments
84    #[deftly(netdoc(subdoc))]
85    pub r: Vec<Router>,
86
87    /// `directory-footer` section (which we handle as a sub-document)
88    #[deftly(netdoc(subdoc))]
89    pub directory_footer: Option<NddDirectoryFooter>,
90}
91
92/// `vote-status` value
93///
94/// In a non-demo we'd probably abolish this,
95/// using `NdaStatus` directly in `NddNetworkStatus`
96/// impl of `ItemValueParseable` for tuples.
97#[derive(Deftly, Clone, Debug, Hash, Eq, PartialEq)]
98#[derive_deftly(ItemValueParseable)]
99#[non_exhaustive]
100pub struct NdiVoteStatus {
101    /// status
102    pub status: ns_type!(VoteStatusVote, VoteStatusConsensus, VoteStatusConsensus),
103}
104
105/// `voting-delay` value
106#[derive(Deftly, Clone, Debug, Hash, Eq, PartialEq)]
107#[derive_deftly(ItemValueParseable)]
108#[non_exhaustive]
109pub struct NdiVotingDelay {
110    /// VoteSeconds
111    pub vote_seconds: u32,
112    /// DistSeconds
113    pub dist_seconds: u32,
114}
115
116/// `dir-source`
117#[derive(Deftly, Clone, Debug)]
118#[derive_deftly(ItemValueParseable)]
119#[non_exhaustive]
120pub struct NdiAuthorityDirSource {
121    /// nickname
122    pub nickname: types::Nickname,
123    /// fingerprint
124    pub h_p_auth_id_rsa: types::Fingerprint,
125}
126
127ns_choose! { (
128    use VoteAuthoritySection as NddAuthoritySection;
129)(
130    use ConsensusAuthoritySection as NddAuthoritySection;
131)}
132
133ns_choose! { (
134    impl NetworkStatusUnverified {
135        /// Verify this vote's signatures using the embedded certificate
136        ///
137        /// # Security considerations
138        ///
139        /// The caller should use `NetworkStatus::h_kp_auth_id_rsa`
140        /// to find out which voter's vote this is.
141        pub fn verify_selfcert(
142            self,
143            now: SystemTime,
144        ) -> Result<(NetworkStatus, SignaturesData<NetworkStatusUnverified>), VF> {
145            let validity = *self.body.published.0 ..= *self.body.valid_until.0;
146            check_validity_time(now, validity)?;
147
148            let cert = self.body.parse_authcert()?.verify_selfcert(now)?;
149
150            netstatus::verify_general_timeless(
151                &self.sigs.hashes,
152                slice::from_ref(&self.sigs.sigs.directory_signature),
153                &[*cert.fingerprint],
154                &[&cert],
155                1,
156            )?;
157
158            Ok(self.unwrap_unverified())
159        }
160    }
161
162    impl NetworkStatus {
163        /// Parse the embedded authcert
164        fn parse_authcert(&self) -> Result<crate::doc::authcert::AuthCertUnverified, EP> {
165            let cert_input = ParseInput::new(
166                self.authority.cert.as_str(),
167                "<embedded auth cert>",
168            );
169            parse_netdoc(&cert_input).map_err(|e| e.problem)
170        }
171
172        /// Voter identity
173        ///
174        /// # Security considerations
175        ///
176        /// The returned identity has been confirmed to have properly certified
177        /// this vote at this time.
178        ///
179        /// It is up to the caller to decide whether this identity is actually
180        /// a voter, count up votes, etc.
181        pub fn h_kp_auth_id_rsa(&self) -> pk::rsa::RsaIdentity {
182            *self.parse_authcert()
183                // SECURITY: if the user calls this function, they have a bare
184                // NetworkStatus, not a NetworkStatusUnverified, so parsing
185                // and verification has already been done in verify_selfcert above.
186                .expect("was verified already!")
187                .inspect_unverified()
188                .0
189                .fingerprint
190        }
191    }
192) (
193    impl NetworkStatusUnverified {
194        /// Verify this consensus document
195        ///
196        /// # Security considerations
197        ///
198        /// The timeliness verification is relaxed, and incorporates the `DistSeconds` skew.
199        /// The caller **must not use** the returned consensus before its `valid_after`,
200        /// and must handle `fresh_until`.
201        ///
202        /// `authorities` should be a list of the authorities
203        /// that the caller trusts.
204        ///
205        /// `certs` is a list of dir auth key certificates to use to try to link
206        /// the signed consensus to those authorities.
207        /// Extra certificates in `certs`, that don't come from anyone in `authorities`,
208        /// are ignored.
209        pub fn verify(
210            self,
211            now: SystemTime,
212            authorities: &[pk::rsa::RsaIdentity],
213            certs: &[&DirAuthKeyCert],
214        ) -> Result<(NetworkStatus, SignaturesData<NetworkStatusUnverified>), VF> {
215            let threshold = authorities.len() / 2 + 1; // strict majority
216            let validity_start = self.body.valid_after.0
217                .checked_sub(Duration::from_secs(self.body.voting_delay.dist_seconds.into()))
218                .ok_or(VF::Other)?;
219            check_validity_time(now, validity_start..= *self.body.valid_until.0)?;
220
221            netstatus::verify_general_timeless(
222                &self.sigs.hashes,
223                &self.sigs.sigs.directory_signature,
224                authorities,
225                certs,
226                threshold,
227            )?;
228
229            Ok(self.unwrap_unverified())
230        }
231    }
232)}