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

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

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

            
40
128
    let intro_points = ipt_set
41
128
        .ipts
42
128
        .iter()
43
384
        .map(|ipt_in_set| ipt_in_set.ipt.clone())
44
128
        .collect::<Vec<_>>();
45
128

            
46
128
    let nickname = &config.nickname;
47
128

            
48
128
    let svc_key_spec = HsIdKeypairSpecifier::new(nickname.clone());
49
128
    let hsid_kp = keymgr
50
128
        .get::<HsIdKeypair>(&svc_key_spec)?
51
128
        .ok_or_else(|| FatalError::MissingHsIdKeypair(nickname.clone()))?;
52
128
    let hsid = HsIdKey::from(&hsid_kp);
53
128

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

            
59
128
    let blind_id_key = HsBlindIdKey::from(&blind_id_kp);
60
128
    let subcredential = hsid.compute_subcredential(&blind_id_key, period);
61
128

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

            
69
    // TODO #1028: support introduction-layer authentication.
70
128
    let auth_required = None;
71

            
72
128
    let is_single_onion_service =
73
128
        matches!(config.anonymity, crate::Anonymity::DangerouslyNonAnonymous);
74

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

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

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

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

            
118
128
    let desc = HsDescBuilder::default()
119
128
        .blinded_id(&(&blind_id_kp).into())
120
128
        .hs_desc_sign(hs_desc_sign.as_ref())
121
128
        .hs_desc_sign_cert(desc_signing_key_cert)
122
128
        .create2_formats(CREATE2_FORMATS)
123
128
        .auth_required(auth_required)
124
128
        .is_single_onion_service(is_single_onion_service)
125
128
        .intro_points(&intro_points[..])
126
128
        .intro_auth_key_cert_expiry(intro_auth_key_cert_expiry)
127
128
        .intro_enc_key_cert_expiry(intro_enc_key_cert_expiry)
128
128
        .lifetime(((ipt_set.lifetime.as_secs() / 60) as u16).into())
129
128
        .revision_counter(revision_counter)
130
128
        .subcredential(subcredential)
131
128
        .auth_clients(auth_clients.as_deref())
132
128
        .build_sign(rng)
133
128
        .map_err(|e| into_internal!("failed to build descriptor")(e))?;
134

            
135
128
    Ok(VersionedDescriptor {
136
128
        desc,
137
128
        revision_counter,
138
128
    })
139
128
}
140

            
141
/// The freshness status of a descriptor at a particular HsDir.
142
#[derive(Copy, Clone, Debug, Default, PartialEq)]
143
pub(super) enum DescriptorStatus {
144
    #[default]
145
    /// Dirty, needs to be (re)uploaded.
146
    Dirty,
147
    /// Clean, does not need to be reuploaded.
148
    Clean,
149
}
150

            
151
/// A descriptor and its revision.
152
#[derive(Clone)]
153
pub(super) struct VersionedDescriptor {
154
    /// The serialized descriptor.
155
    pub(super) desc: String,
156
    /// The revision counter.
157
    pub(super) revision_counter: RevisionCounter,
158
}