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::borrow::Borrow;
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
impl ValidUntilChanged {
56
    /// Return true iff at least one key that is used for a relay descriptor has changed.
57
    ///
58
    /// The relay descriptor requires the relay signing key and the ntor key (onion key).
59
    pub(super) fn relay_desc_keys_changed(&self) -> bool {
60
        self.relaysign_ed || self.ntor_latest
61
    }
62
}
63

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

            
83
impl<K: Borrow<KeyMgr>> FullKeyView<K> {
84
    /// Constructor.
85
12
    pub(super) fn new(keymgr: K) -> anyhow::Result<Self> {
86
12
        let mut view = Self {
87
12
            keymgr,
88
12
            keys_valid_until: ValidUntilKeys::default(),
89
12
        };
90
        // Recompute now so we get a coherent cache from what exists in the KeyMgr.
91
12
        view.recompute_valid_until()?;
92

            
93
12
        Ok(view)
94
12
    }
95

            
96
    /// Return a reference to the key manager.
97
    pub(super) fn keymgr(&self) -> &KeyMgr {
98
        self.keymgr.borrow()
99
    }
100

            
101
    /// Rebuild the valid_until cache from the current keystore state.
102
    ///
103
    /// Reads all expirable key types from the keystore and replaces the cache. For ntor keys,
104
    /// entries are sorted descending so the newest is `ntor_latest` and the second (if any) is
105
    /// `ntor_previous`.
106
    ///
107
    /// Returns a view of which key valid_until has changed.
108
24
    pub(super) fn recompute_valid_until(&mut self) -> anyhow::Result<ValidUntilChanged> {
109
24
        let mut cache = ValidUntilKeys::default();
110

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

            
121
24
        if let Some(entry) = self
122
24
            .keymgr
123
24
            .borrow()
124
24
            .list_matching(&RelaySigningKeypairSpecifierPattern::new_any().arti_pattern()?)?
125
24
            .first()
126
        {
127
            cache.relaysign_ed =
128
10
                Some(RelaySigningKeypairSpecifier::try_from(entry.key_path())?.valid_until);
129
14
        }
130

            
131
24
        let mut ntor: Vec<Timestamp> = self
132
24
            .keymgr
133
24
            .borrow()
134
24
            .list_matching(&RelayNtorKeypairSpecifierPattern::new_any().arti_pattern()?)?
135
24
            .iter()
136
24
            .map(|entry| Ok(RelayNtorKeypairSpecifier::try_from(entry.key_path())?.valid_until))
137
24
            .collect::<anyhow::Result<_>>()?;
138
        // Sort in descending order.
139
24
        ntor.sort_by(|a, b| b.cmp(a));
140
24
        cache.ntor_latest = ntor.first().copied();
141
24
        cache.ntor_previous = ntor.get(1).copied();
142

            
143
        // Do we have another key after that and if yes, warn that too many exists.
144
24
        if ntor.get(2).is_some() {
145
            tracing::warn!(
146
                "Found more than 2 NTor keys in the keystore. This is not supposed to happen. Latest two will be used"
147
            );
148
24
        }
149

            
150
24
        let changed = ValidUntilChanged {
151
24
            link_ed: self.keys_valid_until.link_ed != cache.link_ed,
152
24
            relaysign_ed: self.keys_valid_until.relaysign_ed != cache.relaysign_ed,
153
24
            ntor_latest: self.keys_valid_until.ntor_latest != cache.ntor_latest,
154
24
            ntor_previous: self.keys_valid_until.ntor_previous != cache.ntor_previous,
155
24
        };
156

            
157
24
        self.keys_valid_until = cache;
158
24
        Ok(changed)
159
24
    }
160

            
161
    /// Return the relay ed25519 identity keypair (KS_relayid_ed).
162
4
    pub(super) fn ks_relayid_ed(&self) -> Result<RelayIdentityKeypair> {
163
4
        self.keymgr
164
4
            .borrow()
165
4
            .get(&RelayIdentityKeypairSpecifier::new())?
166
4
            .context("Missing Ed25519 identity")
167
4
    }
168

            
169
    /// Return the relay RSA identity keypair (KS_relayid_rsa).
170
4
    pub(super) fn ks_relayid_rsa(&self) -> Result<RelayIdentityRsaKeypair> {
171
4
        self.keymgr
172
4
            .borrow()
173
4
            .get(&RelayIdentityRsaKeypairSpecifier::new())?
174
4
            .context("Missing RSA identity")
175
4
    }
176

            
177
    /// Return the link authentication keypair (KS_link_ed).
178
4
    pub(super) fn ks_link_ed(&self) -> Result<RelayLinkSigningKeypair> {
179
4
        let valid_until = self
180
4
            .keys_valid_until
181
4
            .link_ed
182
4
            .ok_or(anyhow::anyhow!("No link authentication key"))?;
183
4
        self.keymgr
184
4
            .borrow()
185
4
            .get(&RelayLinkSigningKeypairSpecifier::new(valid_until))?
186
4
            .context("Missing link authentication key")
187
4
    }
188

            
189
    /// Return the latest and previous ntor keypairs from the keystore (KS_ntor).
190
4
    pub(super) fn ks_ntor_keys(&self) -> anyhow::Result<RelayNtorKeys> {
191
4
        let valid_until = self
192
4
            .keys_valid_until
193
4
            .ntor_latest
194
4
            .ok_or(anyhow::anyhow!("No latest ntor key"))?;
195
4
        let latest = self
196
4
            .keymgr
197
4
            .borrow()
198
4
            .get(&RelayNtorKeypairSpecifier::new(valid_until))?
199
4
            .context("Missing latest ntor key")?;
200
4
        let mut keys = RelayNtorKeys::new(latest);
201

            
202
        // Might not have a previous all the time.
203
4
        if let Some(valid_until) = self.keys_valid_until.ntor_previous {
204
            let previous = self
205
                .keymgr
206
                .borrow()
207
                .get(&RelayNtorKeypairSpecifier::new(valid_until))?
208
                .context("Missing previous ntor key")?;
209
            keys = keys.with_previous(previous);
210
4
        }
211
4
        Ok(keys)
212
4
    }
213

            
214
    /// Return the relay signing key (KS_relaysign_ed).
215
4
    pub(super) fn ks_relaysign_ed(&self) -> Result<RelaySigningKeypair> {
216
4
        let valid_until = self
217
4
            .keys_valid_until
218
4
            .relaysign_ed
219
4
            .ok_or(anyhow::anyhow!("No relay signing key"))?;
220
4
        self.keymgr
221
4
            .borrow()
222
4
            .get(&RelaySigningKeypairSpecifier::new(valid_until))?
223
4
            .context("Missing relay signing key")
224
4
    }
225

            
226
    /// Return the relay signing key certificate.
227
4
    pub(super) fn cert_relaysign_ed(&self) -> Result<RelaySigningKeyCert> {
228
4
        let valid_until = self
229
4
            .keys_valid_until
230
4
            .relaysign_ed
231
4
            .ok_or(anyhow::anyhow!("No relay signing key"))?;
232
4
        let (_key, cert) = self
233
4
            .keymgr
234
4
            .borrow()
235
4
            .get_key_and_cert::<RelaySigningKeypair, RelaySigningKeyCert>(
236
4
                &RelaySigningKeyCertSpecifier::new(RelaySigningPublicKeySpecifier::new(
237
4
                    valid_until,
238
4
                )),
239
4
                &RelayIdentityKeypairSpecifier::new(),
240
            )?
241
4
            .context("Missing relaysign_ed key and cert")?;
242
4
        Ok(cert)
243
4
    }
244
}
245

            
246
#[cfg(test)]
247
mod test {
248
    // @@ begin test lint list maintained by maint/add_warning @@
249
    #![allow(clippy::bool_assert_comparison)]
250
    #![allow(clippy::clone_on_copy)]
251
    #![allow(clippy::dbg_macro)]
252
    #![allow(clippy::mixed_attributes_style)]
253
    #![allow(clippy::print_stderr)]
254
    #![allow(clippy::print_stdout)]
255
    #![allow(clippy::single_char_pattern)]
256
    #![allow(clippy::unwrap_used)]
257
    #![allow(clippy::unchecked_time_subtraction)]
258
    #![allow(clippy::useless_vec)]
259
    #![allow(clippy::needless_pass_by_value)]
260
    #![allow(clippy::string_slice)] // See arti#2571
261
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
262
    //!
263
    use super::*;
264

            
265
    use tor_keymgr::{KeyMgr, KeystoreSelector};
266
    use tor_relay_crypto::pk::{RelayLinkSigningKeypair, RelayNtorKeypair, RelaySigningKeypair};
267

            
268
    use crate::{
269
        keys::{
270
            RelayLinkSigningKeypairSpecifier, RelayNtorKeypairSpecifier,
271
            RelaySigningKeypairSpecifier, Timestamp,
272
        },
273
        tasks::crypto::{keys::generate_key, test::new_keymgr},
274
    };
275

            
276
    fn ts(offset: u64) -> Timestamp {
277
        Timestamp::from(std::time::UNIX_EPOCH + std::time::Duration::from_secs(offset))
278
    }
279

            
280
    fn insert_link_key(keymgr: &KeyMgr, valid_until: Timestamp) {
281
        generate_key::<RelayLinkSigningKeypair>(
282
            keymgr,
283
            &RelayLinkSigningKeypairSpecifier::new(valid_until),
284
        )
285
        .unwrap();
286
    }
287

            
288
    fn insert_signing_key(keymgr: &KeyMgr, valid_until: Timestamp) {
289
        generate_key::<RelaySigningKeypair>(
290
            keymgr,
291
            &RelaySigningKeypairSpecifier::new(valid_until),
292
        )
293
        .unwrap();
294
    }
295

            
296
    fn insert_ntor_key(keymgr: &KeyMgr, valid_until: Timestamp) {
297
        generate_key::<RelayNtorKeypair>(keymgr, &RelayNtorKeypairSpecifier::new(valid_until))
298
            .unwrap();
299
    }
300

            
301
    /// Reconciling after keys are added should report them as changed.
302
    #[test]
303
    fn reconcile_new_keys() {
304
        let keymgr = new_keymgr();
305
        let mut view = FullKeyView::new(&keymgr).unwrap();
306

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

            
311
        let changed = view.recompute_valid_until().unwrap();
312

            
313
        assert!(changed.link_ed);
314
        assert!(changed.relaysign_ed);
315
        assert!(changed.ntor_latest);
316
        assert!(!changed.ntor_previous);
317
    }
318

            
319
    /// Reconciling twice without any keystore changes should report nothing.
320
    #[test]
321
    fn reconcile_no_change() {
322
        let keymgr = new_keymgr();
323
        let mut view = FullKeyView::new(&keymgr).unwrap();
324

            
325
        insert_link_key(&keymgr, ts(1000));
326
        insert_signing_key(&keymgr, ts(2000));
327
        insert_ntor_key(&keymgr, ts(3000));
328

            
329
        view.recompute_valid_until().unwrap();
330

            
331
        let changed = view.recompute_valid_until().unwrap();
332
        assert!(
333
            !changed.link_ed
334
                && !changed.relaysign_ed
335
                && !changed.ntor_latest
336
                && !changed.ntor_previous
337
        );
338
    }
339

            
340
    /// With two ntor keys, the one with the higher timestamp becomes ntor_latest and the
341
    /// lower one becomes ntor_previous.
342
    #[test]
343
    fn reconcile_ntor_keys() {
344
        let keymgr = new_keymgr();
345
        let mut view = FullKeyView::new(&keymgr).unwrap();
346

            
347
        let older_ts = ts(1000);
348
        let newer_ts = ts(2000);
349

            
350
        insert_ntor_key(&keymgr, older_ts);
351
        insert_ntor_key(&keymgr, newer_ts);
352

            
353
        let changed = view.recompute_valid_until().unwrap();
354

            
355
        assert!(changed.ntor_latest);
356
        assert!(changed.ntor_previous);
357
        assert_eq!(view.keys_valid_until.ntor_latest, Some(newer_ts));
358
        assert_eq!(view.keys_valid_until.ntor_previous, Some(older_ts));
359
    }
360

            
361
    /// After a key rotation the replaced key type appears in the changed set.
362
    #[test]
363
    fn reconcile_rotated_key() {
364
        let keymgr = new_keymgr();
365
        let mut view = FullKeyView::new(&keymgr).unwrap();
366

            
367
        insert_link_key(&keymgr, ts(1000));
368

            
369
        view.recompute_valid_until().unwrap();
370

            
371
        // Simulate rotation: old key is removed and a new one is inserted.
372
        keymgr
373
            .remove::<RelayLinkSigningKeypair>(
374
                &RelayLinkSigningKeypairSpecifier::new(ts(1000)),
375
                KeystoreSelector::default(),
376
            )
377
            .unwrap();
378
        insert_link_key(&keymgr, ts(5000));
379

            
380
        let changed = view.recompute_valid_until().unwrap();
381

            
382
        assert!(changed.link_ed);
383
        assert_eq!(view.keys_valid_until.link_ed, Some(ts(5000)));
384
    }
385
}