1
//! network status documents: shared between votes, consensuses and md consensuses
2

            
3
use super::*;
4

            
5
use crate::doc::{self, authcert};
6
use crate::types;
7
use authcert::{AuthCert as DirAuthKeyCert, AuthCertKeyIds};
8
pub use doc::netstatus::Signature as NdiDirectorySignature;
9
use doc::netstatus::{
10
    ConsensusAuthoritySection, DirectorySignaturesHashesAccu, VoteAuthoritySection,
11
};
12

            
13
mod ns_per_flavour_macros;
14
pub use ns_per_flavour_macros::*;
15

            
16
ns_per_flavour_macros::ns_export_flavoured_types! {
17
    NetworkStatus, NetworkStatusUnverified, Router,
18
}
19

            
20
/// `network-status-version` version value
21
#[derive(Debug, Clone, Copy, Eq, PartialEq, strum::EnumString, strum::Display)]
22
#[non_exhaustive]
23
pub enum NdaNetworkStatusVersion {
24
    /// The currently supported version, `3`
25
    #[strum(serialize = "3")]
26
    V3,
27
}
28

            
29
impl NormalItemArgument for NdaNetworkStatusVersion {}
30

            
31
/// `params` value
32
#[derive(Clone, Debug, Default, Deftly)]
33
#[derive_deftly(ItemValueParseable)]
34
#[non_exhaustive]
35
pub struct NdiParams {
36
    // Not implemented.
37
}
38

            
39
/// `r` sub-document
40
#[derive(Deftly, Clone, Debug)]
41
#[derive_deftly(ItemValueParseable)]
42
#[non_exhaustive]
43
pub struct NdiR {
44
    /// nickname
45
    pub nickname: types::Nickname,
46
    /// identity
47
    pub identity: String, // In non-demo, use a better type
48
}
49

            
50
/// Unsupported `vote-status` value
51
///
52
/// This message is not normally actually shown since our `ErrorProblem` doesn't contain it.
53
#[derive(Clone, Debug, Error)]
54
#[non_exhaustive]
55
#[error("invalid value for vote-status in network status document")]
56
pub struct InvalidNetworkStatusVoteStatus {}
57

            
58
/// Meat of the verification functions for network documents
59
///
60
/// Checks that at least `threshold` members of `trusted`
61
/// have signed this document (in `signatures`),
62
/// via some cert(s) in `certs`.
63
///
64
/// Does not check validity time.
65
2
fn verify_general_timeless(
66
2
    hashes: &DirectorySignaturesHashesAccu,
67
2
    signatures: &[NdiDirectorySignature],
68
2
    trusted: &[pk::rsa::RsaIdentity],
69
2
    certs: &[&DirAuthKeyCert],
70
2
    threshold: usize,
71
2
) -> Result<(), VF> {
72
2
    let mut ok = HashSet::<pk::rsa::RsaIdentity>::new();
73

            
74
8
    for sig in signatures {
75
        let NdiDirectorySignature {
76
8
            digest_algo: hash_algo,
77
            key_ids:
78
                AuthCertKeyIds {
79
8
                    id_fingerprint: h_kp_auth_id_rsa,
80
8
                    sk_fingerprint: h_kp_auth_sign_rsa,
81
                },
82
8
            signature: rsa_signature,
83
8
        } = sig;
84

            
85
8
        if let Some(h) = hashes.hash_slice_for_verification(hash_algo) {
86
8
            let Some(authority) = ({
87
8
                trusted
88
8
                    .iter()
89
24
                    .find(|trusted| **trusted == *h_kp_auth_id_rsa)
90
            }) else {
91
                // unknown kp_auth_id_rsa, ignore it
92
                continue;
93
            };
94
8
            let Some(cert) = ({
95
8
                certs
96
8
                    .iter()
97
24
                    .find(|cert| cert.dir_signing_key.to_rsa_identity() == *h_kp_auth_sign_rsa)
98
            }) else {
99
                // no cert for this kp_auth_sign_rsa, ignore it
100
                continue;
101
            };
102

            
103
8
            let () = cert.dir_signing_key.verify(h, rsa_signature)?;
104

            
105
8
            ok.insert(*authority);
106
        }
107
    }
108

            
109
2
    if ok.len() < threshold {
110
        return Err(VF::InsufficientTrustedSigners);
111
2
    }
112

            
113
2
    Ok(())
114
2
}