Skip to main content

tor_hsservice/
keys.rs

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
12use 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.
20pub(crate) trait HsTimePeriodKeySpecifier: Debug {
21    /// Inspect the nickname
22    fn nickname(&self) -> &HsNickname;
23    /// Inspect the period
24    fn period(&self) -> &TimePeriod;
25}
26
27define_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.
49pub 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.
61pub struct HsIdKeypairSpecifier {
62    /// The nickname of the  hidden service.
63    pub(crate) nickname: HsNickname,
64}
65
66impl From<&HsIdPublicKeySpecifier> for HsIdKeypairSpecifier {
67    fn from(hs_id_public_key_specifier: &HsIdPublicKeySpecifier) -> HsIdKeypairSpecifier {
68        HsIdKeypairSpecifier::new(hs_id_public_key_specifier.nickname.clone())
69    }
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.
78pub 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.
93pub 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
101impl 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.
118pub 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")]
129pub(crate) enum IptKeyRole {
130    /// `k_hss_ntor`
131    KHssNtor,
132    /// `k_hss_ntor`
133    KSid,
134}
135
136impl 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")]
143pub(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
156pub(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)]
172mod 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}