1
//! Helpers for building and representing hidden service descriptors.
2

            
3
use super::*;
4
use crate::config::OnionServiceConfigPublisherView;
5
use tor_cell::chancell::msg::HandshakeType;
6
use tor_llcrypto::rng::EntropicRng;
7

            
8
/// Build the descriptor.
9
///
10
/// The `now` argument is used for computing the expiry of the `intro_{auth, enc}_key_cert`
11
/// certificates included in the descriptor. The expiry will be set to 54 hours from `now`.
12
///
13
/// Note: `blind_id_kp` is the blinded hidden service signing keypair used to sign descriptor
14
/// signing keys (KP_hs_blind_id, KS_hs_blind_id).
15
#[allow(clippy::too_many_arguments)]
16
pub(super) fn build_sign<Rng: RngCore + CryptoRng, KeyRng: RngCore + EntropicRng, R: Runtime>(
17
    keymgr: &Arc<KeyMgr>,
18
    pow_manager: &Arc<PowManager<R>>,
19
    config: &Arc<OnionServiceConfigPublisherView>,
20
    authorized_clients: Option<&RestrictedDiscoveryKeys>,
21
    ipt_set: &IptSet,
22
    period: TimePeriod,
23
    revision_counter: RevisionCounter,
24
    rng: &mut Rng,
25
    key_rng: &mut KeyRng,
26
    now: SystemTime,
27
) -> Result<VersionedDescriptor, FatalError> {
28
    // TODO: should this be configurable? If so, we should read it from the svc config.
29
    //
30
    /// The CREATE handshake type we support.
31
    const CREATE2_FORMATS: &[HandshakeType] = &[HandshakeType::NTOR];
32

            
33
    /// Lifetime of the intro_{auth, enc}_key_cert certificates in the descriptor.
34
    ///
35
    /// From C-Tor src/feature/hs/hs_descriptor.h:
36
    ///
37
    /// "This defines the lifetime of the descriptor signing key and the cross certification cert of
38
    /// that key. It is set to 54 hours because a descriptor can be around for 48 hours and because
39
    /// consensuses are used after the hour, add an extra 6 hours to give some time for the service
40
    /// to stop using it."
41
    const HS_DESC_CERT_LIFETIME_SEC: Duration = Duration::from_secs(54 * 60 * 60);
42

            
43
    let intro_points = ipt_set
44
        .ipts
45
        .iter()
46
        .map(|ipt_in_set| ipt_in_set.ipt.clone())
47
        .collect::<Vec<_>>();
48

            
49
    let nickname = &config.nickname;
50

            
51
    let svc_key_spec = HsIdPublicKeySpecifier::new(nickname.clone());
52
    let hsid = keymgr
53
        .get::<HsIdKey>(&svc_key_spec)?
54
        .ok_or_else(|| FatalError::MissingHsIdKeypair(nickname.clone()))?;
55

            
56
    // TODO: make the keystore selector configurable
57
    let keystore_selector = Default::default();
58
    let blind_id_kp = read_blind_id_keypair(keymgr, nickname, period)?
59
        .ok_or_else(|| internal!("hidden service offline mode not supported"))?;
60

            
61
    let blind_id_key = HsBlindIdKey::from(&blind_id_kp);
62
    let subcredential = hsid.compute_subcredential(&blind_id_key, period);
63

            
64
    let hs_desc_sign_key_spec = DescSigningKeypairSpecifier::new(nickname.clone(), period);
65
    let hs_desc_sign = keymgr.get_or_generate::<HsDescSigningKeypair>(
66
        &hs_desc_sign_key_spec,
67
        keystore_selector,
68
        key_rng,
69
    )?;
70

            
71
    // TODO #1028: support introduction-layer authentication.
72
    let auth_required = None;
73

            
74
    // TODO(#727): add support for single onion services
75
    let is_single_onion_service = false;
76

            
77
    // TODO (#955): perhaps the certificates should be read from the keystore, rather than created
78
    // when building the descriptor. See #1048
79
    let intro_auth_key_cert_expiry = now + HS_DESC_CERT_LIFETIME_SEC;
80
    let intro_enc_key_cert_expiry = now + HS_DESC_CERT_LIFETIME_SEC;
81
    let hs_desc_sign_cert_expiry = now + HS_DESC_CERT_LIFETIME_SEC;
82

            
83
    cfg_if::cfg_if! {
84
        if #[cfg(feature = "restricted-discovery")] {
85
            let auth_clients: Option<Vec<curve25519::PublicKey>> = authorized_clients
86
                .as_ref()
87
                .map(|authorized_clients| {
88
                    if authorized_clients.is_empty() {
89
                        return Err(internal!("restricted discovery enabled, but no authorized clients?!"));
90
                    }
91
                    let auth_clients = authorized_clients
92
                        .iter()
93
                        .map(|(nickname, key)| {
94
                            trace!("encrypting descriptor for client {nickname}");
95
                            (*key).clone().into()
96
                        })
97
                        .collect_vec();
98
                    Ok(auth_clients)
99
                })
100
                .transpose()?;
101
        } else {
102
            let auth_clients: Option<Vec<curve25519::PublicKey>> = None;
103
        }
104
    }
105

            
106
    if let Some(ref auth_clients) = auth_clients {
107
        debug!("Encrypting descriptor for {} clients", auth_clients.len());
108
    }
109

            
110
    let desc_signing_key_cert = create_desc_sign_key_cert(
111
        &hs_desc_sign.as_ref().verifying_key(),
112
        &blind_id_kp,
113
        hs_desc_sign_cert_expiry,
114
    )
115
    .map_err(into_bad_api_usage!(
116
        "failed to sign the descriptor signing key"
117
    ))?;
118

            
119
    let blind_id_kp = (&blind_id_kp).into();
120

            
121
    let mut desc = HsDescBuilder::default()
122
        .blinded_id(&blind_id_kp)
123
        .hs_desc_sign(hs_desc_sign.as_ref())
124
        .hs_desc_sign_cert(desc_signing_key_cert)
125
        .create2_formats(CREATE2_FORMATS)
126
        .auth_required(auth_required)
127
        .is_single_onion_service(is_single_onion_service)
128
        .intro_points(&intro_points[..])
129
        .intro_auth_key_cert_expiry(intro_auth_key_cert_expiry)
130
        .intro_enc_key_cert_expiry(intro_enc_key_cert_expiry)
131
        .lifetime(((ipt_set.lifetime.as_secs() / 60) as u16).into())
132
        .revision_counter(revision_counter)
133
        .subcredential(subcredential)
134
        .auth_clients(auth_clients.as_deref());
135

            
136
    cfg_if::cfg_if! {
137
        if #[cfg(feature = "hs-pow-full")] {
138
            let pow_params = pow_manager.get_pow_params(period);
139
            match pow_params {
140
                Ok(ref pow_params) => {
141
                    desc = desc.pow_params(Some(pow_params));
142
                },
143
                Err(err) => {
144
                    warn!(?err, "Couldn't get PoW params");
145
                }
146
            }
147
        }
148
    }
149

            
150
    let desc = desc
151
        .build_sign(rng)
152
        .map_err(|e| into_internal!("failed to build descriptor")(e))?;
153

            
154
    Ok(VersionedDescriptor {
155
        desc,
156
        revision_counter,
157
    })
158
}
159

            
160
/// The freshness status of a descriptor at a particular HsDir.
161
#[derive(Copy, Clone, Debug, Default, PartialEq)]
162
pub(super) enum DescriptorStatus {
163
    #[default]
164
    /// Dirty, needs to be (re)uploaded.
165
    Dirty,
166
    /// Clean, does not need to be reuploaded.
167
    Clean,
168
}
169

            
170
/// A descriptor and its revision.
171
#[derive(Clone)]
172
pub(super) struct VersionedDescriptor {
173
    /// The serialized descriptor.
174
    pub(super) desc: String,
175
    /// The revision counter.
176
    pub(super) revision_counter: RevisionCounter,
177
}