1
//! Information about directory authorities
2
//!
3
//! From a client's point of view, an authority's role is to sign the
4
//! consensus directory.
5

            
6
use derive_builder::Builder;
7
use serde::{Deserialize, Serialize};
8
use tor_config::{define_list_builder_helper, impl_standard_builder, ConfigBuildError};
9
use tor_llcrypto::pk::rsa::RsaIdentity;
10

            
11
/// A single authority that signs a consensus directory.
12
//
13
// Note that we do *not* set serde(deny_unknown_fields)] on this structure:
14
// we want our authorities format to be future-proof against adding new info
15
// about each authority.
16
87880
#[derive(Debug, Clone, Builder, Eq, PartialEq)]
17
#[builder(build_fn(error = "ConfigBuildError"))]
18
#[builder(derive(Debug, Serialize, Deserialize))]
19
pub struct Authority {
20
    /// A memorable nickname for this authority.
21
    #[builder(setter(into))]
22
    name: String,
23
    /// A SHA1 digest of the DER-encoded long-term v3 RSA identity key for
24
    /// this authority.
25
    // TODO: It would be lovely to use a better hash for these identities.
26
    #[cfg(not(feature = "experimental-api"))]
27
    pub(crate) v3ident: RsaIdentity,
28
    #[cfg(feature = "experimental-api")]
29
    /// A SHA1 digest of the DER-encoded long-term v3 RSA identity key for
30
    /// this authority.
31
    pub v3ident: RsaIdentity,
32
}
33

            
34
impl_standard_builder! { Authority: !Default }
35

            
36
/// Authority list, built
37
pub(crate) type AuthorityList = Vec<Authority>;
38

            
39
define_list_builder_helper! {
40
    pub(crate) struct AuthorityListBuilder {
41
        authorities: [AuthorityBuilder],
42
    }
43
    built: AuthorityList = authorities;
44
    default = default_authorities();
45
}
46

            
47
/// Return a vector of the default directory authorities.
48
2430
pub(crate) fn default_authorities() -> Vec<AuthorityBuilder> {
49
    /// Build an authority; panic if input is bad.
50
21870
    fn auth(name: &str, key: &str) -> AuthorityBuilder {
51
21870
        let v3ident =
52
21870
            RsaIdentity::from_hex(key).expect("Built-in authority identity had bad hex!?");
53
21870
        let mut auth = AuthorityBuilder::new();
54
21870
        auth.name(name).v3ident(v3ident);
55
21870
        auth
56
21870
    }
57

            
58
    // (Last updated on May 30th, 2024)
59
2430
    vec![
60
2430
        auth("bastet", "27102BC123E7AF1D4741AE047E160C91ADC76B21"),
61
2430
        auth("dannenberg", "0232AF901C31A04EE9848595AF9BB7620D4C5B2E"),
62
2430
        auth("dizum", "E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58"),
63
2430
        auth("faravahar", "70849B868D606BAECFB6128C5E3D782029AA394F"),
64
2430
        auth("gabelmoo", "ED03BB616EB2F60BEC80151114BB25CEF515B226"),
65
2430
        auth("longclaw", "23D15D965BC35114467363C165C4F724B64B4F66"),
66
2430
        auth("maatuska", "49015F787433103580E3B66A1707A00E60F2D15B"),
67
2430
        auth("moria1", "F533C81CEF0BC0267857C99B2F471ADF249FA232"),
68
2430
        auth("tor26", "2F3DF9CA0E5D36F2685A2DA67184EB8DCB8CBA8C"),
69
2430
    ]
70
2430
}
71

            
72
impl AuthorityBuilder {
73
    /// Make a new AuthorityBuilder with no fields set.
74
21870
    pub fn new() -> Self {
75
21870
        Self::default()
76
21870
    }
77
}
78

            
79
#[cfg(test)]
80
mod test {
81
    // @@ begin test lint list maintained by maint/add_warning @@
82
    #![allow(clippy::bool_assert_comparison)]
83
    #![allow(clippy::clone_on_copy)]
84
    #![allow(clippy::dbg_macro)]
85
    #![allow(clippy::mixed_attributes_style)]
86
    #![allow(clippy::print_stderr)]
87
    #![allow(clippy::print_stdout)]
88
    #![allow(clippy::single_char_pattern)]
89
    #![allow(clippy::unwrap_used)]
90
    #![allow(clippy::unchecked_duration_subtraction)]
91
    #![allow(clippy::useless_vec)]
92
    #![allow(clippy::needless_pass_by_value)]
93
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
94
    use super::*;
95
    use tor_netdoc::doc::authcert::AuthCertKeyIds;
96

            
97
    impl Authority {
98
        /// Return true if this authority matches a given key ID.
99
        fn matches_keyid(&self, id: &AuthCertKeyIds) -> bool {
100
            self.v3ident == id.id_fingerprint
101
        }
102
    }
103

            
104
    #[test]
105
    fn authority() {
106
        let key1: RsaIdentity = [9_u8; 20].into();
107
        let key2: RsaIdentity = [10_u8; 20].into();
108
        let auth = Authority::builder()
109
            .name("example")
110
            .v3ident(key1)
111
            .build()
112
            .unwrap();
113

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

            
116
        let keyids1 = AuthCertKeyIds {
117
            id_fingerprint: key1,
118
            sk_fingerprint: key2,
119
        };
120
        assert!(auth.matches_keyid(&keyids1));
121

            
122
        let keyids2 = AuthCertKeyIds {
123
            id_fingerprint: key2,
124
            sk_fingerprint: key2,
125
        };
126
        assert!(!auth.matches_keyid(&keyids2));
127
    }
128

            
129
    #[test]
130
    fn auth() {
131
        let dflt = AuthorityListBuilder::default().build().unwrap();
132
        assert_eq!(&dflt[0].name[..], "bastet");
133
        assert_eq!(
134
            &dflt[0].v3ident.to_string()[..],
135
            "$27102bc123e7af1d4741ae047e160c91adc76b21"
136
        );
137
    }
138
}