1
//! [`KeySpecifier`] implementations for hidden service keys.
2
//!
3
//! Some of these `KeySpecifier`s represent time-bound keys (that are only valid
4
//! as long as their time period is relevant). Time-bound keys are expired (removed)
5
//! by [`expire_publisher_keys`].
6
//!
7
//! If you add a new key that is not a per-service singleton, you also need to
8
//! make arrangements to delete old ones.
9
//! For TP-based keys, that involves deriving [`HsTimePeriodKeySpecifier`]
10
//! and adding a call to `remove_if_expired!` in [`expire_publisher_keys`].
11

            
12
use crate::{internal_prelude::*, list_expired_keys_for_service};
13

            
14
/// Keys that are used by publisher, which relate to our HS and a TP
15
///
16
/// Derived using
17
/// the derive-deftly macro of the same name.
18
// We'd like to link to crate::derive_deftly_template_HsTimePeriodKeySpecifier
19
// but linking to a module-local macro doesn't work with rustdoc.
20
pub(crate) trait HsTimePeriodKeySpecifier: Debug {
21
    /// Inspect the nickname
22
    fn nickname(&self) -> &HsNickname;
23
    /// Inspect the period
24
    fn period(&self) -> &TimePeriod;
25
}
26

            
27
define_derive_deftly! {
28
    /// Implement `HsTimePeriodKeySpecifier` for a struct with `nickname` and `period`
29
    HsTimePeriodKeySpecifier:
30

            
31
    impl HsTimePeriodKeySpecifier for $ttype {
32
      $(
33
        ${when any(approx_equal($fname, nickname), approx_equal($fname, period))}
34
        fn $fname(&self) -> &$ftype {
35
            &self.$fname
36
        }
37
      )
38
    }
39
}
40

            
41
#[derive(Deftly, PartialEq, Debug, Constructor)]
42
#[derive_deftly(KeySpecifier)]
43
#[deftly(prefix = "hss")]
44
#[deftly(role = "KP_hs_id")]
45
#[deftly(summary = "Public part of the identity key")]
46
#[deftly(keypair_specifier = "HsIdKeypairSpecifier")]
47
#[deftly(ctor_path = "HsIdPublicKey")]
48
/// The public part of the identity key of the service.
49
pub struct HsIdPublicKeySpecifier {
50
    /// The nickname of the  hidden service.
51
    nickname: HsNickname,
52
}
53

            
54
#[derive(Deftly, PartialEq, Debug, Constructor)]
55
#[derive_deftly(KeySpecifier)]
56
#[deftly(prefix = "hss")]
57
#[deftly(role = "KS_hs_id")]
58
#[deftly(summary = "Long-term identity keypair")]
59
#[deftly(ctor_path = "HsIdKeypair")]
60
/// The long-term identity keypair of the service.
61
pub struct HsIdKeypairSpecifier {
62
    /// The nickname of the  hidden service.
63
    pub(crate) nickname: HsNickname,
64
}
65

            
66
impl From<&HsIdPublicKeySpecifier> for HsIdKeypairSpecifier {
67
230
    fn from(hs_id_public_key_specifier: &HsIdPublicKeySpecifier) -> HsIdKeypairSpecifier {
68
230
        HsIdKeypairSpecifier::new(hs_id_public_key_specifier.nickname.clone())
69
230
    }
70
}
71

            
72
#[derive(Deftly, PartialEq, Debug, Constructor)]
73
#[derive_deftly(KeySpecifier, HsTimePeriodKeySpecifier)]
74
#[deftly(prefix = "hss")]
75
#[deftly(role = "KS_hs_blind_id")]
76
#[deftly(summary = "Blinded signing keypair")]
77
/// The blinded signing keypair.
78
pub struct BlindIdKeypairSpecifier {
79
    /// The nickname of the  hidden service.
80
    pub(crate) nickname: HsNickname,
81
    #[deftly(denotator)]
82
    /// The time period associated with this key.
83
    pub(crate) period: TimePeriod,
84
}
85

            
86
#[derive(Deftly, PartialEq, Debug, Constructor)]
87
#[derive_deftly(KeySpecifier, HsTimePeriodKeySpecifier)]
88
#[deftly(prefix = "hss")]
89
#[deftly(role = "KP_hs_blind_id")]
90
#[deftly(keypair_specifier = "BlindIdKeypairSpecifier")]
91
#[deftly(summary = "Blinded public key")]
92
/// The blinded public key.
93
pub struct BlindIdPublicKeySpecifier {
94
    /// The nickname of the  hidden service.
95
    pub(crate) nickname: HsNickname,
96
    #[deftly(denotator)]
97
    /// The time period associated with this key.
98
    pub(crate) period: TimePeriod,
99
}
100

            
101
impl From<&BlindIdPublicKeySpecifier> for BlindIdKeypairSpecifier {
102
    fn from(
103
        hs_blind_id_public_key_specifier: &BlindIdPublicKeySpecifier,
104
    ) -> BlindIdKeypairSpecifier {
105
        BlindIdKeypairSpecifier::new(
106
            hs_blind_id_public_key_specifier.nickname.clone(),
107
            hs_blind_id_public_key_specifier.period,
108
        )
109
    }
110
}
111

            
112
#[derive(Deftly, PartialEq, Debug, Constructor)]
113
#[derive_deftly(KeySpecifier, HsTimePeriodKeySpecifier)]
114
#[deftly(prefix = "hss")]
115
#[deftly(role = "KS_hs_desc_sign")]
116
#[deftly(summary = "Descriptor signing key")]
117
/// The descriptor signing key.
118
pub struct DescSigningKeypairSpecifier {
119
    /// The nickname of the  hidden service.
120
    pub(crate) nickname: HsNickname,
121
    #[deftly(denotator)]
122
    /// The time period associated with this key.
123
    pub(crate) period: TimePeriod,
124
}
125

            
126
/// Denotates one of the keys, in the context of a particular HS and intro point
127
#[derive(Debug, Deftly, Eq, PartialEq, strum::Display, strum::EnumString)]
128
#[strum(serialize_all = "snake_case")]
129
pub(crate) enum IptKeyRole {
130
    /// `k_hss_ntor`
131
    KHssNtor,
132
    /// `k_hss_ntor`
133
    KSid,
134
}
135

            
136
impl KeySpecifierComponentViaDisplayFromStr for IptKeyRole {}
137

            
138
/// Specifies an intro point key
139
#[derive(Debug, Deftly, Eq, PartialEq)]
140
#[derive_deftly(KeySpecifier)]
141
#[deftly(prefix = "hss")]
142
#[deftly(summary = "introduction point key")]
143
pub(crate) struct IptKeySpecifier {
144
    /// nick
145
    pub(crate) nick: HsNickname,
146
    /// which key
147
    #[deftly(fixed_path_component = "ipts")]
148
    #[deftly(role)]
149
    pub(crate) role: IptKeyRole,
150
    /// lid
151
    #[deftly(denotator)]
152
    pub(crate) lid: IptLocalId,
153
}
154

            
155
/// Expire publisher keys for no-longer relevant TPs
156
pub(crate) fn expire_publisher_keys(
157
    keymgr: &KeyMgr,
158
    nickname: &HsNickname,
159
    relevant_periods: &[HsDirParams],
160
) -> tor_keymgr::Result<()> {
161
    // TODO: any invalid/malformed keys are ignored (rather than
162
    // removed).
163
    let keys_to_remove = list_expired_keys_for_service(relevant_periods, nickname, keymgr)?;
164
    for entry in keys_to_remove {
165
        keymgr.remove_entry(&entry)?;
166
    }
167

            
168
    Ok(())
169
}
170

            
171
#[cfg(test)]
172
mod test {
173
    // @@ begin test lint list maintained by maint/add_warning @@
174
    #![allow(clippy::bool_assert_comparison)]
175
    #![allow(clippy::clone_on_copy)]
176
    #![allow(clippy::dbg_macro)]
177
    #![allow(clippy::mixed_attributes_style)]
178
    #![allow(clippy::print_stderr)]
179
    #![allow(clippy::print_stdout)]
180
    #![allow(clippy::single_char_pattern)]
181
    #![allow(clippy::unwrap_used)]
182
    #![allow(clippy::unchecked_time_subtraction)]
183
    #![allow(clippy::useless_vec)]
184
    #![allow(clippy::needless_pass_by_value)]
185
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
186
    use super::*;
187
    use tor_keymgr::KeySpecifier;
188
    use tor_keymgr::test_utils::check_key_specifier;
189

            
190
    #[test]
191
    fn hsid_key_specifiers() {
192
        let nickname = HsNickname::try_from("shallot".to_string()).unwrap();
193
        let key_spec = HsIdPublicKeySpecifier::new(nickname.clone());
194
        assert_eq!(
195
            key_spec.arti_path().unwrap().as_str(),
196
            "hss/shallot/kp_hs_id"
197
        );
198

            
199
        let key_spec = HsIdKeypairSpecifier::new(nickname);
200
        check_key_specifier(&key_spec, "hss/shallot/ks_hs_id");
201
    }
202

            
203
    #[test]
204
    fn blind_id_key_specifiers() {
205
        let nickname = HsNickname::try_from("shallot".to_string()).unwrap();
206
        let period = TimePeriod::from_parts(1, 2, 3);
207
        let key_spec = BlindIdPublicKeySpecifier::new(nickname.clone(), period);
208
        assert_eq!(
209
            key_spec.arti_path().unwrap().as_str(),
210
            "hss/shallot/kp_hs_blind_id+2_1_3"
211
        );
212

            
213
        let key_spec = BlindIdKeypairSpecifier::new(nickname, period);
214
        check_key_specifier(&key_spec, "hss/shallot/ks_hs_blind_id+2_1_3");
215
    }
216

            
217
    #[test]
218
    fn desc_signing_key_specifiers() {
219
        let nickname = HsNickname::try_from("shallot".to_string()).unwrap();
220
        let period = TimePeriod::from_parts(1, 2, 3);
221
        let key_spec = DescSigningKeypairSpecifier::new(nickname, period);
222
        check_key_specifier(&key_spec, "hss/shallot/ks_hs_desc_sign+2_1_3");
223
    }
224

            
225
    #[test]
226
    fn ipt_key_specifiers() {
227
        let nick = HsNickname::try_from("shallot".to_string()).unwrap();
228
        let lid = IptLocalId::dummy(1);
229
        let spec = |role| IptKeySpecifier {
230
            nick: nick.clone(),
231
            lid,
232
            role,
233
        };
234
        let lid_s = "0101010101010101010101010101010101010101010101010101010101010101";
235
        check_key_specifier(
236
            &spec(IptKeyRole::KHssNtor),
237
            &format!("hss/shallot/ipts/k_hss_ntor+{lid_s}"),
238
        );
239
        check_key_specifier(
240
            &spec(IptKeyRole::KSid),
241
            &format!("hss/shallot/ipts/k_sid+{lid_s}"),
242
        );
243
    }
244
}