1
//! Views that restricts the access to only specific keys which are tailored for specific tasks.
2
//! The domain specific views use the generic view helper which wraps the [`KeyMgr`].
3

            
4
use anyhow::{Context, Result};
5
use std::sync::Arc;
6

            
7
use tor_keymgr::{KeyMgr, KeySpecifierPattern};
8
use tor_relay_crypto::{
9
    RelaySigningKeyCert,
10
    pk::{
11
        RelayIdentityKeypair, RelayIdentityRsaKeypair, RelayLinkSigningKeypair, RelayNtorKeys,
12
        RelaySigningKeypair,
13
    },
14
};
15

            
16
use crate::keys::{
17
    RelayIdentityKeypairSpecifier, RelayIdentityRsaKeypairSpecifier,
18
    RelayLinkSigningKeypairSpecifier, RelayLinkSigningKeypairSpecifierPattern,
19
    RelayNtorKeypairSpecifier, RelayNtorKeypairSpecifierPattern, RelaySigningKeyCertSpecifier,
20
    RelaySigningKeypairSpecifier, RelaySigningKeypairSpecifierPattern,
21
    RelaySigningPublicKeySpecifier, Timestamp,
22
};
23

            
24
/// Cache of `valid_until` timestamps for each expirable key type.
25
///
26
/// This is used in the [`FullKeyView`] to keep coherence between tasks. Updated by the crypto task
27
/// when keys are generated or rotated.
28
#[derive(Clone, Default)]
29
pub(super) struct ValidUntilKeys {
30
    /// Relay link authentication ed25519 keypair.
31
    pub(super) link_ed: Option<Timestamp>,
32
    /// Relay signing ed25519 keypair.
33
    pub(super) relaysign_ed: Option<Timestamp>,
34
    /// Ntor latest (current) keypair.
35
    pub(super) ntor_latest: Option<Timestamp>,
36
    /// Ntor previous keypair.
37
    pub(super) ntor_previous: Option<Timestamp>,
38
}
39

            
40
/// Indicates which valid_until cache entries changed.
41
///
42
/// This is used when recompute the valid_until cache to indicate to the caller what has changed.
43
#[derive(Default)]
44
pub(super) struct ValidUntilChanged {
45
    /// Relay link authentication ed25519 keypair changed.
46
    pub(super) link_ed: bool,
47
    /// Relay signing ed25519 keypair changed.
48
    pub(super) relaysign_ed: bool,
49
    /// Ntor latest (current) keypair changed.
50
    pub(super) ntor_latest: bool,
51
    /// Ntor previous keypair changed.
52
    pub(super) ntor_previous: bool,
53
}
54

            
55
/// A full view of all relay keys within the [`KeyMgr`] it holds.
56
///
57
/// This keeps the key view that are used accross tasks coherent that is it keeps a cache of
58
/// valid_until value for expirable keys. Only keys of that valid_until are looked for which makes
59
/// that each task will always see the same key when doing a lookup.
60
///
61
/// That valid_until cache is updated by the crypto task when keys are generated/rotated.
62
///
63
/// Domain specific view wrap this view in order to restrict key access.
64
pub(super) struct FullKeyView {
65
    /// The relay key manager.
66
    keymgr: Arc<KeyMgr>,
67
    /// The keys' valid_until cache.
68
    ///
69
    /// This is so we can lookup directly any live key without walking all existing keys and find
70
    /// the earliest valid_until.
71
    keys_valid_until: ValidUntilKeys,
72
}
73

            
74
impl FullKeyView {
75
    /// Constructor.
76
12
    pub(super) fn new(keymgr: Arc<KeyMgr>) -> anyhow::Result<Self> {
77
12
        let mut view = Self {
78
12
            keymgr,
79
12
            keys_valid_until: ValidUntilKeys::default(),
80
12
        };
81
        // Recompute now so we get a coherent cache from what exists in the KeyMgr.
82
12
        view.recompute_valid_until()?;
83

            
84
12
        Ok(view)
85
12
    }
86

            
87
    /// Return a reference to the key manager.
88
    pub(super) fn keymgr(&self) -> &KeyMgr {
89
        &self.keymgr
90
    }
91

            
92
    /// Rebuild the valid_until cache from the current keystore state.
93
    ///
94
    /// Reads all expirable key types from the keystore and replaces the cache. For ntor keys,
95
    /// entries are sorted descending so the newest is `ntor_latest` and the second (if any) is
96
    /// `ntor_previous`.
97
    ///
98
    /// Returns a view of which key valid_until has changed.
99
24
    pub(super) fn recompute_valid_until(&mut self) -> anyhow::Result<ValidUntilChanged> {
100
24
        let mut cache = ValidUntilKeys::default();
101

            
102
24
        if let Some(entry) = self
103
24
            .keymgr
104
24
            .list_matching(&RelayLinkSigningKeypairSpecifierPattern::new_any().arti_pattern()?)?
105
24
            .first()
106
        {
107
            cache.link_ed =
108
14
                Some(RelayLinkSigningKeypairSpecifier::try_from(entry.key_path())?.valid_until);
109
10
        }
110

            
111
24
        if let Some(entry) = self
112
24
            .keymgr
113
24
            .list_matching(&RelaySigningKeypairSpecifierPattern::new_any().arti_pattern()?)?
114
24
            .first()
115
        {
116
            cache.relaysign_ed =
117
10
                Some(RelaySigningKeypairSpecifier::try_from(entry.key_path())?.valid_until);
118
14
        }
119

            
120
24
        let mut ntor: Vec<Timestamp> = self
121
24
            .keymgr
122
24
            .list_matching(&RelayNtorKeypairSpecifierPattern::new_any().arti_pattern()?)?
123
24
            .iter()
124
31
            .map(|entry| Ok(RelayNtorKeypairSpecifier::try_from(entry.key_path())?.valid_until))
125
24
            .collect::<anyhow::Result<_>>()?;
126
        // Sort in descending order.
127
25
        ntor.sort_by(|a, b| b.cmp(a));
128
24
        cache.ntor_latest = ntor.first().copied();
129
24
        cache.ntor_previous = ntor.get(1).copied();
130

            
131
        // Do we have another key after that and if yes, warn that too many exists.
132
24
        if ntor.get(2).is_some() {
133
            tracing::warn!(
134
                "Found more than 2 NTor keys in the keystore. This is not supposed to happen. Latest two will be used"
135
            );
136
24
        }
137

            
138
24
        let changed = ValidUntilChanged {
139
24
            link_ed: self.keys_valid_until.link_ed != cache.link_ed,
140
24
            relaysign_ed: self.keys_valid_until.relaysign_ed != cache.relaysign_ed,
141
24
            ntor_latest: self.keys_valid_until.ntor_latest != cache.ntor_latest,
142
24
            ntor_previous: self.keys_valid_until.ntor_previous != cache.ntor_previous,
143
24
        };
144

            
145
24
        self.keys_valid_until = cache;
146
24
        Ok(changed)
147
24
    }
148

            
149
    /// Return the relay ed25519 identity keypair (KS_relayid_ed).
150
4
    pub(super) fn ks_relayid_ed(&self) -> Result<RelayIdentityKeypair> {
151
4
        self.keymgr
152
4
            .get(&RelayIdentityKeypairSpecifier::new())?
153
4
            .context("Missing Ed25519 identity")
154
4
    }
155

            
156
    /// Return the relay RSA identity keypair (KS_relayid_rsa).
157
4
    pub(super) fn ks_relayid_rsa(&self) -> Result<RelayIdentityRsaKeypair> {
158
4
        self.keymgr
159
4
            .get(&RelayIdentityRsaKeypairSpecifier::new())?
160
4
            .context("Missing RSA identity")
161
4
    }
162

            
163
    /// Return the link authentication keypair (KS_link_ed).
164
4
    pub(super) fn ks_link_ed(&self) -> Result<RelayLinkSigningKeypair> {
165
4
        let valid_until = self
166
4
            .keys_valid_until
167
4
            .link_ed
168
4
            .ok_or(anyhow::anyhow!("No link authentication key"))?;
169
4
        self.keymgr
170
4
            .get(&RelayLinkSigningKeypairSpecifier::new(valid_until))?
171
4
            .context("Missing link authentication key")
172
4
    }
173

            
174
    /// Return the latest and previous ntor keypairs from the keystore (KS_ntor).
175
4
    pub(super) fn ks_ntor_keys(&self) -> anyhow::Result<RelayNtorKeys> {
176
4
        let valid_until = self
177
4
            .keys_valid_until
178
4
            .ntor_latest
179
4
            .ok_or(anyhow::anyhow!("No latest ntor key"))?;
180
4
        let latest = self
181
4
            .keymgr
182
4
            .get(&RelayNtorKeypairSpecifier::new(valid_until))?
183
4
            .context("Missing latest ntor key")?;
184
4
        let mut keys = RelayNtorKeys::new(latest);
185

            
186
        // Might not have a previous all the time.
187
4
        if let Some(valid_until) = self.keys_valid_until.ntor_previous {
188
            let previous = self
189
                .keymgr
190
                .get(&RelayNtorKeypairSpecifier::new(valid_until))?
191
                .context("Missing previous ntor key")?;
192
            keys = keys.with_previous(previous);
193
4
        }
194
4
        Ok(keys)
195
4
    }
196

            
197
    /// Return the relay signing key (KS_relaysign_ed).
198
4
    pub(super) fn ks_relaysign_ed(&self) -> Result<RelaySigningKeypair> {
199
4
        let valid_until = self
200
4
            .keys_valid_until
201
4
            .relaysign_ed
202
4
            .ok_or(anyhow::anyhow!("No relay signing key"))?;
203
4
        self.keymgr
204
4
            .get(&RelaySigningKeypairSpecifier::new(valid_until))?
205
4
            .context("Missing relay signing key")
206
4
    }
207

            
208
    /// Return the relay signing key certificate.
209
4
    pub(super) fn cert_relaysign_ed(&self) -> Result<RelaySigningKeyCert> {
210
4
        let valid_until = self
211
4
            .keys_valid_until
212
4
            .relaysign_ed
213
4
            .ok_or(anyhow::anyhow!("No relay signing key"))?;
214
4
        let (_key, cert) = self
215
4
            .keymgr
216
4
            .get_key_and_cert::<RelaySigningKeypair, RelaySigningKeyCert>(
217
4
                &RelaySigningKeyCertSpecifier::new(RelaySigningPublicKeySpecifier::new(
218
4
                    valid_until,
219
4
                )),
220
4
                &RelayIdentityKeypairSpecifier::new(),
221
4
            )?
222
4
            .context("Missing relaysign_ed key and cert")?;
223
4
        Ok(cert)
224
4
    }
225
}
226

            
227
#[cfg(test)]
228
mod test {
229
    // @@ begin test lint list maintained by maint/add_warning @@
230
    #![allow(clippy::bool_assert_comparison)]
231
    #![allow(clippy::clone_on_copy)]
232
    #![allow(clippy::dbg_macro)]
233
    #![allow(clippy::mixed_attributes_style)]
234
    #![allow(clippy::print_stderr)]
235
    #![allow(clippy::print_stdout)]
236
    #![allow(clippy::single_char_pattern)]
237
    #![allow(clippy::unwrap_used)]
238
    #![allow(clippy::unchecked_time_subtraction)]
239
    #![allow(clippy::useless_vec)]
240
    #![allow(clippy::needless_pass_by_value)]
241
    #![allow(clippy::string_slice)] // See arti#2571
242
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
243
    //!
244
    use super::*;
245

            
246
    use tor_keymgr::{KeyMgr, KeystoreSelector};
247
    use tor_relay_crypto::pk::{RelayLinkSigningKeypair, RelayNtorKeypair, RelaySigningKeypair};
248

            
249
    use crate::{
250
        keys::{
251
            RelayLinkSigningKeypairSpecifier, RelayNtorKeypairSpecifier,
252
            RelaySigningKeypairSpecifier, Timestamp,
253
        },
254
        tasks::crypto::{keys::generate_key, test::new_keymgr},
255
    };
256

            
257
    fn ts(offset: u64) -> Timestamp {
258
        Timestamp::from(std::time::UNIX_EPOCH + std::time::Duration::from_secs(offset))
259
    }
260

            
261
    fn insert_link_key(keymgr: &KeyMgr, valid_until: Timestamp) {
262
        generate_key::<RelayLinkSigningKeypair>(
263
            keymgr,
264
            &RelayLinkSigningKeypairSpecifier::new(valid_until),
265
        )
266
        .unwrap();
267
    }
268

            
269
    fn insert_signing_key(keymgr: &KeyMgr, valid_until: Timestamp) {
270
        generate_key::<RelaySigningKeypair>(
271
            keymgr,
272
            &RelaySigningKeypairSpecifier::new(valid_until),
273
        )
274
        .unwrap();
275
    }
276

            
277
    fn insert_ntor_key(keymgr: &KeyMgr, valid_until: Timestamp) {
278
        generate_key::<RelayNtorKeypair>(keymgr, &RelayNtorKeypairSpecifier::new(valid_until))
279
            .unwrap();
280
    }
281

            
282
    /// Reconciling after keys are added should report them as changed.
283
    #[test]
284
    fn reconcile_new_keys() {
285
        let keymgr = new_keymgr();
286
        let mut view = FullKeyView::new(keymgr.clone()).unwrap();
287

            
288
        insert_link_key(&keymgr, ts(1000));
289
        insert_signing_key(&keymgr, ts(2000));
290
        insert_ntor_key(&keymgr, ts(3000));
291

            
292
        let changed = view.recompute_valid_until().unwrap();
293

            
294
        assert!(changed.link_ed);
295
        assert!(changed.relaysign_ed);
296
        assert!(changed.ntor_latest);
297
        assert!(!changed.ntor_previous);
298
    }
299

            
300
    /// Reconciling twice without any keystore changes should report nothing.
301
    #[test]
302
    fn reconcile_no_change() {
303
        let keymgr = new_keymgr();
304
        let mut view = FullKeyView::new(keymgr.clone()).unwrap();
305

            
306
        insert_link_key(&keymgr, ts(1000));
307
        insert_signing_key(&keymgr, ts(2000));
308
        insert_ntor_key(&keymgr, ts(3000));
309

            
310
        view.recompute_valid_until().unwrap();
311

            
312
        let changed = view.recompute_valid_until().unwrap();
313
        assert!(
314
            !changed.link_ed
315
                && !changed.relaysign_ed
316
                && !changed.ntor_latest
317
                && !changed.ntor_previous
318
        );
319
    }
320

            
321
    /// With two ntor keys, the one with the higher timestamp becomes ntor_latest and the
322
    /// lower one becomes ntor_previous.
323
    #[test]
324
    fn reconcile_ntor_keys() {
325
        let keymgr = new_keymgr();
326
        let mut view = FullKeyView::new(keymgr.clone()).unwrap();
327

            
328
        let older_ts = ts(1000);
329
        let newer_ts = ts(2000);
330

            
331
        insert_ntor_key(&keymgr, older_ts);
332
        insert_ntor_key(&keymgr, newer_ts);
333

            
334
        let changed = view.recompute_valid_until().unwrap();
335

            
336
        assert!(changed.ntor_latest);
337
        assert!(changed.ntor_previous);
338
        assert_eq!(view.keys_valid_until.ntor_latest, Some(newer_ts));
339
        assert_eq!(view.keys_valid_until.ntor_previous, Some(older_ts));
340
    }
341

            
342
    /// After a key rotation the replaced key type appears in the changed set.
343
    #[test]
344
    fn reconcile_rotated_key() {
345
        let keymgr = new_keymgr();
346
        let mut view = FullKeyView::new(keymgr.clone()).unwrap();
347

            
348
        insert_link_key(&keymgr, ts(1000));
349

            
350
        view.recompute_valid_until().unwrap();
351

            
352
        // Simulate rotation: old key is removed and a new one is inserted.
353
        keymgr
354
            .remove::<RelayLinkSigningKeypair>(
355
                &RelayLinkSigningKeypairSpecifier::new(ts(1000)),
356
                KeystoreSelector::default(),
357
            )
358
            .unwrap();
359
        insert_link_key(&keymgr, ts(5000));
360

            
361
        let changed = view.recompute_valid_until().unwrap();
362

            
363
        assert!(changed.link_ed);
364
        assert_eq!(view.keys_valid_until.link_ed, Some(ts(5000)));
365
    }
366
}