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

            
14
use super::super::*;
15

            
16
/// Toplevel document string for error reporting
17
const TOPLEVEL_DOCTYPE_FOR_ERROR: &str =
18
    ns_expr!("NetworkStatusVote", "NetworkStatusNs", "NetworkStatusMd",);
19

            
20
/// The real router status entry type.
21
pub 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.
28
pub 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.
35
pub 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.
42
pub 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]
56
pub 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]
100
pub 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]
109
pub 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]
120
pub struct NdiAuthorityDirSource {
121
    /// nickname
122
    pub nickname: types::Nickname,
123
    /// fingerprint
124
    pub h_p_auth_id_rsa: types::Fingerprint,
125
}
126

            
127
ns_choose! { (
128
    use VoteAuthoritySection as NddAuthoritySection;
129
)(
130
    use ConsensusAuthoritySection as NddAuthoritySection;
131
)}
132

            
133
ns_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
)}