tor_hsservice/
keys.rs
1use tor_keymgr::{CTorPath, CTorServicePath};
13
14use crate::internal_prelude::*;
15
16trait HsTimePeriodKeySpecifier: Debug {
23 fn nickname(&self) -> &HsNickname;
25 fn period(&self) -> &TimePeriod;
27}
28
29define_derive_deftly! {
30 HsTimePeriodKeySpecifier:
32
33 impl HsTimePeriodKeySpecifier for $ttype {
34 $(
35 ${when any(approx_equal($fname, nickname), approx_equal($fname, period))}
36 fn $fname(&self) -> &$ftype {
37 &self.$fname
38 }
39 )
40 }
41}
42
43#[derive(Deftly, PartialEq, Debug, Constructor)]
44#[derive_deftly(KeySpecifier)]
45#[deftly(prefix = "hss")]
46#[deftly(role = "KP_hs_id")]
47#[deftly(summary = "Public part of the identity key")]
48#[deftly(keypair_specifier = "HsIdKeypairSpecifier")]
49#[deftly(ctor_path = "hsid_public_key_specifier_ctor_path")]
50pub struct HsIdPublicKeySpecifier {
52 nickname: HsNickname,
54}
55
56fn hsid_public_key_specifier_ctor_path(spec: &HsIdPublicKeySpecifier) -> CTorPath {
58 CTorPath::Service {
59 nickname: spec.nickname.clone(),
60 path: CTorServicePath::PublicKey,
61 }
62}
63
64#[derive(Deftly, PartialEq, Debug, Constructor)]
65#[derive_deftly(KeySpecifier)]
66#[deftly(prefix = "hss")]
67#[deftly(role = "KS_hs_id")]
68#[deftly(summary = "Long-term identity keypair")]
69#[deftly(ctor_path = "hsid_keypair_key_specifier_ctor_path")]
70pub struct HsIdKeypairSpecifier {
72 pub(crate) nickname: HsNickname,
74}
75
76impl From<&HsIdPublicKeySpecifier> for HsIdKeypairSpecifier {
77 fn from(hs_id_public_key_specifier: &HsIdPublicKeySpecifier) -> HsIdKeypairSpecifier {
78 HsIdKeypairSpecifier::new(hs_id_public_key_specifier.nickname.clone())
79 }
80}
81
82fn hsid_keypair_key_specifier_ctor_path(spec: &HsIdKeypairSpecifier) -> CTorPath {
84 CTorPath::Service {
85 nickname: spec.nickname.clone(),
86 path: CTorServicePath::PrivateKey,
87 }
88}
89
90#[derive(Deftly, PartialEq, Debug, Constructor)]
91#[derive_deftly(KeySpecifier, HsTimePeriodKeySpecifier)]
92#[deftly(prefix = "hss")]
93#[deftly(role = "KS_hs_blind_id")]
94#[deftly(summary = "Blinded signing keypair")]
95pub struct BlindIdKeypairSpecifier {
97 pub(crate) nickname: HsNickname,
99 #[deftly(denotator)]
100 pub(crate) period: TimePeriod,
102}
103
104#[derive(Deftly, PartialEq, Debug, Constructor)]
105#[derive_deftly(KeySpecifier, HsTimePeriodKeySpecifier)]
106#[deftly(prefix = "hss")]
107#[deftly(role = "KP_hs_blind_id")]
108#[deftly(keypair_specifier = "BlindIdKeypairSpecifier")]
109#[deftly(summary = "Blinded public key")]
110pub struct BlindIdPublicKeySpecifier {
112 pub(crate) nickname: HsNickname,
114 #[deftly(denotator)]
115 pub(crate) period: TimePeriod,
117}
118
119impl From<&BlindIdPublicKeySpecifier> for BlindIdKeypairSpecifier {
120 fn from(
121 hs_blind_id_public_key_specifier: &BlindIdPublicKeySpecifier,
122 ) -> BlindIdKeypairSpecifier {
123 BlindIdKeypairSpecifier::new(
124 hs_blind_id_public_key_specifier.nickname.clone(),
125 hs_blind_id_public_key_specifier.period,
126 )
127 }
128}
129
130#[derive(Deftly, PartialEq, Debug, Constructor)]
131#[derive_deftly(KeySpecifier, HsTimePeriodKeySpecifier)]
132#[deftly(prefix = "hss")]
133#[deftly(role = "KS_hs_desc_sign")]
134#[deftly(summary = "Descriptor signing key")]
135pub struct DescSigningKeypairSpecifier {
137 pub(crate) nickname: HsNickname,
139 #[deftly(denotator)]
140 pub(crate) period: TimePeriod,
142}
143
144#[derive(Debug, Deftly, Eq, PartialEq, strum::Display, strum::EnumString)]
146#[strum(serialize_all = "snake_case")]
147pub(crate) enum IptKeyRole {
148 KHssNtor,
150 KSid,
152}
153
154impl KeySpecifierComponentViaDisplayFromStr for IptKeyRole {}
155
156#[derive(Debug, Deftly, Eq, PartialEq)]
158#[derive_deftly(KeySpecifier)]
159#[deftly(prefix = "hss")]
160#[deftly(summary = "introduction point key")]
161pub(crate) struct IptKeySpecifier {
162 pub(crate) nick: HsNickname,
164 #[deftly(fixed_path_component = "ipts")]
166 #[deftly(role)]
167 pub(crate) role: IptKeyRole,
168 #[deftly(denotator)]
170 pub(crate) lid: IptLocalId,
171}
172
173pub(crate) fn expire_publisher_keys(
175 keymgr: &KeyMgr,
176 nickname: &HsNickname,
177 relevant_periods: &[HsDirParams],
178) -> tor_keymgr::Result<()> {
179 let arti_pat = tor_keymgr::KeyPathPattern::Arti(format!("hss/{}/*", &nickname));
182 let possibly_relevant_keys = keymgr.list_matching(&arti_pat)?;
183
184 for entry in possibly_relevant_keys {
185 let key_path = entry.key_path();
186 let remove_if_expired = |spec: &dyn HsTimePeriodKeySpecifier| {
188 if spec.nickname() != nickname {
189 return Err(internal!(
190 "keymgr gave us key {spec:?} that doesn't match our pattern {arti_pat:?}"
191 )
192 .into());
193 }
194 let is_expired = relevant_periods
195 .iter()
196 .all(|p| &p.time_period() != spec.period());
197
198 if is_expired {
199 keymgr.remove_entry(&entry)?;
200 }
201
202 tor_keymgr::Result::Ok(())
203 };
204
205 macro_rules! remove_if_expired {
207 ($K:ty) => {{
208 if let Ok(spec) = <$K>::try_from(key_path) {
209 remove_if_expired(&spec)?;
210 }
211 }};
212 }
213
214 remove_if_expired!(BlindIdPublicKeySpecifier);
217 remove_if_expired!(BlindIdKeypairSpecifier);
218 remove_if_expired!(DescSigningKeypairSpecifier);
219 }
220
221 Ok(())
222}
223
224#[cfg(test)]
225mod test {
226 #![allow(clippy::bool_assert_comparison)]
228 #![allow(clippy::clone_on_copy)]
229 #![allow(clippy::dbg_macro)]
230 #![allow(clippy::mixed_attributes_style)]
231 #![allow(clippy::print_stderr)]
232 #![allow(clippy::print_stdout)]
233 #![allow(clippy::single_char_pattern)]
234 #![allow(clippy::unwrap_used)]
235 #![allow(clippy::unchecked_duration_subtraction)]
236 #![allow(clippy::useless_vec)]
237 #![allow(clippy::needless_pass_by_value)]
238 use super::*;
240 use tor_keymgr::test_utils::check_key_specifier;
241 use tor_keymgr::KeySpecifier;
242
243 #[test]
244 fn hsid_key_specifiers() {
245 let nickname = HsNickname::try_from("shallot".to_string()).unwrap();
246 let key_spec = HsIdPublicKeySpecifier::new(nickname.clone());
247 assert_eq!(
248 key_spec.arti_path().unwrap().as_str(),
249 "hss/shallot/kp_hs_id"
250 );
251
252 let key_spec = HsIdKeypairSpecifier::new(nickname);
253 check_key_specifier(&key_spec, "hss/shallot/ks_hs_id");
254 }
255
256 #[test]
257 fn blind_id_key_specifiers() {
258 let nickname = HsNickname::try_from("shallot".to_string()).unwrap();
259 let period = TimePeriod::from_parts(1, 2, 3);
260 let key_spec = BlindIdPublicKeySpecifier::new(nickname.clone(), period);
261 assert_eq!(
262 key_spec.arti_path().unwrap().as_str(),
263 "hss/shallot/kp_hs_blind_id+2_1_3"
264 );
265
266 let key_spec = BlindIdKeypairSpecifier::new(nickname, period);
267 check_key_specifier(&key_spec, "hss/shallot/ks_hs_blind_id+2_1_3");
268 }
269
270 #[test]
271 fn desc_signing_key_specifiers() {
272 let nickname = HsNickname::try_from("shallot".to_string()).unwrap();
273 let period = TimePeriod::from_parts(1, 2, 3);
274 let key_spec = DescSigningKeypairSpecifier::new(nickname, period);
275 check_key_specifier(&key_spec, "hss/shallot/ks_hs_desc_sign+2_1_3");
276 }
277
278 #[test]
279 fn ipt_key_specifiers() {
280 let nick = HsNickname::try_from("shallot".to_string()).unwrap();
281 let lid = IptLocalId::dummy(1);
282 let spec = |role| IptKeySpecifier {
283 nick: nick.clone(),
284 lid,
285 role,
286 };
287 let lid_s = "0101010101010101010101010101010101010101010101010101010101010101";
288 check_key_specifier(
289 &spec(IptKeyRole::KHssNtor),
290 &format!("hss/shallot/ipts/k_hss_ntor+{lid_s}"),
291 );
292 check_key_specifier(
293 &spec(IptKeyRole::KSid),
294 &format!("hss/shallot/ipts/k_sid+{lid_s}"),
295 );
296 }
297}