summaryrefslogtreecommitdiff
path: root/tor-dirmgr/src/authority.rs
blob: c671d931f71c7161dd7508f563d941aaffde5cac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//! Information about directory authorities
//!
//! From a client's point of view, an authority's role is to to sign the
//! consensus directory.

use crate::{Error, Result};
use serde::Deserialize;
use tor_llcrypto::pk::rsa::RsaIdentity;
use tor_netdoc::doc::authcert::{AuthCert, AuthCertKeyIds};

/// A single authority that signs a consensus directory.
//
// Note that we do *not* set serde(deny_unknown_fields)] on this structure:
// we want our authorities format to be future-proof against adding new info
// about each authority.
#[derive(Deserialize, Debug, Clone)]
pub struct Authority {
    /// A memorable nickname for this authority.
    name: String,
    /// A SHA1 digest of the DER-encoded long-term v3 RSA identity key for
    /// this authority.
    // TODO: It would be lovely to use a better hash for these identities.
    v3ident: RsaIdentity,
}

impl Authority {
    /// Return a new builder for constructing an [`Authority`].
    ///
    /// You only need this if you're using a non-default Tor network
    /// with its own set of directory authorities.
    pub fn builder() -> AuthorityBuilder {
        AuthorityBuilder::new()
    }
    /// Return the v3 identity key of this certificate.
    ///
    /// This is the identity of the >=2048-bit RSA key that the
    /// authority uses to sign documents; it is distinct from its
    /// identity keys that it uses when operating as a relay.
    pub fn v3ident(&self) -> &RsaIdentity {
        &self.v3ident
    }
    /// Return true if this authority matches a given certificate.
    pub fn matches_cert(&self, cert: &AuthCert) -> bool {
        &self.v3ident == cert.id_fingerprint()
    }

    /// Return true if this authority matches a given key ID.
    pub fn matches_keyid(&self, id: &AuthCertKeyIds) -> bool {
        self.v3ident == id.id_fingerprint
    }
}

/// Return a vector of the default directory authorities.
pub(crate) fn default_authorities() -> Vec<Authority> {
    /// Build an authority; panic if input is bad.
    fn auth(name: &str, key: &str) -> Authority {
        let v3ident = hex::decode(key).expect("Built-in authority identity had bad hex!?");
        let v3ident = RsaIdentity::from_bytes(&v3ident)
            .expect("Built-in authority identity had wrong length!?");
        AuthorityBuilder::new()
            .name(name)
            .v3ident(v3ident)
            .build()
            .expect("unable to construct built-in authority!?")
    }

    // (List generated August 2020.)
    vec![
        auth("bastet", "27102BC123E7AF1D4741AE047E160C91ADC76B21"),
        auth("dannenberg", "0232AF901C31A04EE9848595AF9BB7620D4C5B2E"),
        auth("dizum", "E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58"),
        auth("Faravahar", "EFCBE720AB3A82B99F9E953CD5BF50F7EEFC7B97"),
        auth("gabelmoo", "ED03BB616EB2F60BEC80151114BB25CEF515B226"),
        auth("longclaw", "23D15D965BC35114467363C165C4F724B64B4F66"),
        auth("maatuska", "49015F787433103580E3B66A1707A00E60F2D15B"),
        auth("moria1", "D586D18309DED4CD6D57C18FDB97EFA96D330566"),
        auth("tor26", "14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4"),
    ]
}

/// A Builder object for constructing an [`Authority`] entry.
#[derive(Debug, Clone, Default)]
pub struct AuthorityBuilder {
    /// See [`Authority::name`]
    name: Option<String>,
    /// See [`Authority::v3ident`]
    v3ident: Option<RsaIdentity>,
}

impl AuthorityBuilder {
    /// Make a new AuthorityBuilder with no fields set.
    pub fn new() -> Self {
        Self::default()
    }
    /// Set the name on this AuthorityBuilder.
    ///
    /// This field is required.
    pub fn name(&mut self, name: &str) -> &mut Self {
        self.name = Some(name.to_owned());
        self
    }
    /// Set the v3 RSA identity of this AuthorityBuilder.
    ///
    /// This field is required.
    pub fn v3ident(&mut self, key: RsaIdentity) -> &mut Self {
        self.v3ident = Some(key);
        self
    }
    /// Try to build an [`Authority`].
    pub fn build(&self) -> Result<Authority> {
        let name = self
            .name
            .as_ref()
            .ok_or(Error::BadNetworkConfig("Missing authority name"))?
            .clone();
        let v3ident = self
            .v3ident
            .ok_or(Error::BadNetworkConfig("Missing v3 identity key."))?;
        Ok(Authority { name, v3ident })
    }
}

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn authority() {
        let key1: RsaIdentity = [9_u8; 20].into();
        let key2: RsaIdentity = [10_u8; 20].into();
        let auth = Authority::builder()
            .name("example")
            .v3ident(key1)
            .build()
            .unwrap();

        assert_eq!(auth.v3ident(), &key1);

        let keyids1 = AuthCertKeyIds {
            id_fingerprint: key1.clone(),
            sk_fingerprint: key2.clone(),
        };
        assert!(auth.matches_keyid(&keyids1));

        let keyids2 = AuthCertKeyIds {
            id_fingerprint: key2.clone(),
            sk_fingerprint: key2.clone(),
        };
        assert!(!auth.matches_keyid(&keyids2));
    }

    #[test]
    fn auth() {
        let dflt = default_authorities();
        assert_eq!(&dflt[0].name[..], "bastet");
        assert_eq!(
            &dflt[0].v3ident.to_string()[..],
            "$27102bc123e7af1d4741ae047e160c91adc76b21"
        );
    }
}