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
90400
#[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
2500
pub(crate) fn default_authorities() -> Vec<AuthorityBuilder> {
49
    /// Build an authority; panic if input is bad.
50
22500
    fn auth(name: &str, key: &str) -> AuthorityBuilder {
51
22500
        let v3ident =
52
22500
            RsaIdentity::from_hex(key).expect("Built-in authority identity had bad hex!?");
53
22500
        let mut auth = AuthorityBuilder::new();
54
22500
        auth.name(name).v3ident(v3ident);
55
22500
        auth
56
22500
    }
57

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

            
72
impl AuthorityBuilder {
73
    /// Make a new AuthorityBuilder with no fields set.
74
22500
    pub fn new() -> Self {
75
22500
        Self::default()
76
22500
    }
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
}