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            )?;
156
157            Ok(self.unwrap_unverified())
158        }
159    }
160
161    impl NetworkStatus {
162        /// Parse the embedded authcert
163        //
164        // TODO DIRAUTH abolish/move
165        fn parse_authcert(&self) -> Result<crate::doc::authcert::AuthCertUnverified, EP> {
166            let cert_input = ParseInput::new(
167                self.authority.cert.raw_unverified().as_str(),
168                "<embedded auth cert>",
169            );
170            parse_netdoc(&cert_input).map_err(|e| e.problem)
171        }
172
173        /// Voter identity
174        ///
175        /// # Security considerations
176        ///
177        /// The returned identity has been confirmed to have properly certified
178        /// this vote at this time.
179        ///
180        /// It is up to the caller to decide whether this identity is actually
181        /// a voter, count up votes, etc.
182        //
183        // TODO DIRAUTH use EmbeddedCert::get
184        pub fn h_kp_auth_id_rsa(&self) -> pk::rsa::RsaIdentity {
185            *self.parse_authcert()
186                // SECURITY: if the user calls this function, they have a bare
187                // NetworkStatus, not a NetworkStatusUnverified, so parsing
188                // and verification has already been done in verify_selfcert above.
189                .expect("was verified already!")
190                .inspect_unverified()
191                .0
192                .fingerprint
193        }
194    }
195) (
196    impl NetworkStatusUnverified {
197        /// Verify this consensus document
198        ///
199        /// # Security considerations
200        ///
201        /// The timeliness verification is relaxed, and incorporates the `DistSeconds` skew.
202        /// The caller **must not use** the returned consensus before its `valid_after`,
203        /// and must handle `fresh_until`.
204        ///
205        /// `authorities` should be a list of the authorities
206        /// that the caller trusts.
207        ///
208        /// `certs` is a list of dir auth key certificates to use to try to link
209        /// the signed consensus to those authorities.
210        /// Extra certificates in `certs`, that don't come from anyone in `authorities`,
211        /// are ignored.
212        pub fn verify(
213            self,
214            now: SystemTime,
215            authorities: &[pk::rsa::RsaIdentity],
216            certs: &[&DirAuthKeyCert],
217        ) -> Result<(NetworkStatus, SignaturesData<NetworkStatusUnverified>), VF> {
218            let validity_start = self.body.valid_after.0
219                .checked_sub(Duration::from_secs(self.body.voting_delay.dist_seconds.into()))
220                .ok_or(VF::Other)?;
221            check_validity_time(now, validity_start..= *self.body.valid_until.0)?;
222
223            netstatus::verify_general_timeless(
224                &self.sigs.hashes,
225                &self.sigs.sigs.directory_signature,
226                authorities,
227                certs,
228            )?;
229
230            Ok(self.unwrap_unverified())
231        }
232    }
233)}