tor_hsservice/publish/
descriptor.rs1use super::*;
4use crate::config::OnionServiceConfigPublisherView;
5use tor_cell::chancell::msg::HandshakeType;
6use tor_llcrypto::rng::EntropicRng;
7use tor_netdir::params::NetParameters;
8
9#[allow(clippy::too_many_arguments)]
17pub(super) fn build_sign<
18 Rng: rand::Rng + CryptoRng,
19 KeyRng: rand::Rng + EntropicRng,
20 R: Runtime,
21>(
22 keymgr: &Arc<KeyMgr>,
23 pow_manager: &Arc<PowManager<R>>,
24 config: &Arc<OnionServiceConfigPublisherView>,
25 netparams: &NetParameters,
26 authorized_clients: Option<&RestrictedDiscoveryKeys>,
27 ipt_set: &IptSet,
28 period: TimePeriod,
29 revision_counter: RevisionCounter,
30 rng: &mut Rng,
31 key_rng: &mut KeyRng,
32 now: SystemTime,
33 max_hsdesc_len: usize,
34) -> Result<VersionedDescriptor, FatalError> {
35 const CREATE2_FORMATS: &[HandshakeType] = &[HandshakeType::NTOR];
42
43 const HS_DESC_CERT_LIFETIME_SEC: Duration = Duration::from_secs(54 * 60 * 60);
52
53 let intro_points = ipt_set
54 .ipts
55 .iter()
56 .map(|ipt_in_set| ipt_in_set.ipt.clone())
57 .collect::<Vec<_>>();
58
59 let nickname = &config.nickname;
60
61 let svc_key_spec = HsIdPublicKeySpecifier::new(nickname.clone());
62 let hsid = keymgr
63 .get::<HsIdKey>(&svc_key_spec)?
64 .ok_or_else(|| FatalError::MissingHsIdKeypair(nickname.clone()))?;
65
66 let keystore_selector = Default::default();
68 let blind_id_kp = read_blind_id_keypair(keymgr, nickname, period)?
69 .ok_or_else(|| internal!("hidden service offline mode not supported"))?;
70
71 let blind_id_key = HsBlindIdKey::from(&blind_id_kp);
72 let subcredential = hsid.compute_subcredential(&blind_id_key, period);
73
74 let hs_desc_sign_key_spec = DescSigningKeypairSpecifier::new(nickname.clone(), period);
75 let hs_desc_sign = keymgr.get_or_generate::<HsDescSigningKeypair>(
76 &hs_desc_sign_key_spec,
77 keystore_selector,
78 key_rng,
79 )?;
80
81 let auth_required = None;
83
84 let is_single_onion_service = false;
86
87 let intro_auth_key_cert_expiry = now + HS_DESC_CERT_LIFETIME_SEC;
90 let intro_enc_key_cert_expiry = now + HS_DESC_CERT_LIFETIME_SEC;
91 let hs_desc_sign_cert_expiry = now + HS_DESC_CERT_LIFETIME_SEC;
92
93 cfg_if::cfg_if! {
94 if #[cfg(feature = "restricted-discovery")] {
95 let auth_clients: Option<Vec<curve25519::PublicKey>> = authorized_clients
96 .as_ref()
97 .map(|authorized_clients| {
98 if authorized_clients.is_empty() {
99 return Err(internal!("restricted discovery enabled, but no authorized clients?!"));
100 }
101 let auth_clients = authorized_clients
102 .iter()
103 .map(|(nickname, key)| {
104 trace!("encrypting descriptor for client {nickname}");
105 (*key).clone().into()
106 })
107 .collect_vec();
108 Ok(auth_clients)
109 })
110 .transpose()?;
111 } else {
112 let auth_clients: Option<Vec<curve25519::PublicKey>> = None;
113 }
114 }
115
116 if let Some(ref auth_clients) = auth_clients {
117 debug!("Encrypting descriptor for {} clients", auth_clients.len());
118 }
119
120 let desc_signing_key_cert = create_desc_sign_key_cert(
121 &hs_desc_sign.as_ref().verifying_key(),
122 &blind_id_kp,
123 hs_desc_sign_cert_expiry,
124 )
125 .map_err(into_bad_api_usage!(
126 "failed to sign the descriptor signing key"
127 ))?;
128
129 let blind_id_kp = (&blind_id_kp).into();
130
131 let mut desc = HsDescBuilder::default()
132 .blinded_id(&blind_id_kp)
133 .hs_desc_sign(hs_desc_sign.as_ref())
134 .hs_desc_sign_cert(desc_signing_key_cert)
135 .create2_formats(CREATE2_FORMATS)
136 .auth_required(auth_required)
137 .is_single_onion_service(is_single_onion_service)
138 .intro_points(&intro_points[..])
139 .intro_auth_key_cert_expiry(intro_auth_key_cert_expiry)
140 .intro_enc_key_cert_expiry(intro_enc_key_cert_expiry)
141 .lifetime(((ipt_set.lifetime.as_secs() / 60) as u16).into())
142 .revision_counter(revision_counter)
143 .subcredential(subcredential)
144 .auth_clients(auth_clients.as_deref())
145 .max_generated_len(max_hsdesc_len);
146
147 #[cfg(feature = "negotiate-extensions")]
148 {
149 use crate::caps;
151
152 desc = desc.flow_control(caps::declared_flowctrl(netparams));
161
162 desc = desc.supported_protocols(caps::declared_protocols());
163 }
164
165 cfg_if::cfg_if! {
166 if #[cfg(feature = "hs-pow-full")] {
167 let pow_params = pow_manager.get_pow_params(period, &mut rand::rng());
168 match pow_params {
169 Ok(ref pow_params) => {
170 if config.enable_pow {
171 desc = desc.pow_params(Some(pow_params));
172 }
173 },
174 Err(err) => {
175 warn!(?err, "Couldn't get PoW params");
176 }
177 }
178 }
179 }
180
181 let desc = desc.build_sign(rng).map_err(|e| match e {
182 tor_bytes::EncodeError::BadLengthValue => FatalError::HsDescTooLong,
183 e => into_internal!("failed to build descriptor")(e).into(),
184 })?;
185
186 Ok(VersionedDescriptor {
187 desc,
188 revision_counter,
189 })
190}
191
192#[derive(Copy, Clone, Debug, Default, PartialEq)]
194pub(super) enum DescriptorStatus {
195 #[default]
196 Dirty,
198 Clean,
200}
201
202#[derive(Clone)]
204pub(super) struct VersionedDescriptor {
205 pub(super) desc: String,
207 pub(super) revision_counter: RevisionCounter,
209}