1
//! Key rotation tasks of the relay.
2

            
3
use anyhow::Context;
4
use std::{
5
    sync::Arc,
6
    time::{Duration, SystemTime},
7
};
8
use tor_basic_utils::rand_hostname;
9
use tor_cert::x509::TlsKeyAndCert;
10
use tor_chanmgr::ChanMgr;
11
use tor_error::internal;
12
use tor_key_forge::ToEncodableCert;
13
use tor_keymgr::{
14
    CertSpecifierPattern, KeyCertificateSpecifier, KeyMgr, KeyPath, KeySpecifier,
15
    KeySpecifierPattern, Keygen, KeystoreSelector, ToEncodableKey,
16
};
17
use tor_proto::RelayIdentities;
18
use tor_relay_crypto::{
19
    RelaySigningKeyCert, gen_link_cert, gen_signing_cert, gen_tls_cert,
20
    pk::{
21
        RelayIdentityKeypair, RelayIdentityKeypairSpecifier, RelayIdentityRsaKeypair,
22
        RelayIdentityRsaKeypairSpecifier, RelayLinkSigningKeypair,
23
        RelayLinkSigningKeypairSpecifier, RelayLinkSigningKeypairSpecifierPattern,
24
        RelaySigningKeyCertSpecifier, RelaySigningKeyCertSpecifierPattern, RelaySigningKeypair,
25
        RelaySigningKeypairSpecifier, RelaySigningKeypairSpecifierPattern,
26
        RelaySigningPublicKeySpecifier, Timestamp,
27
    },
28
};
29
use tor_rtcompat::{Runtime, SleepProviderExt};
30

            
31
/// Buffer time before key expiry to trigger rotation. This ensures we rotate slightly before the
32
/// key actually expires rather than right at or after expiry.
33
///
34
/// C-tor uses 3 hours for the link/auth key and 1 day for the signing key. Let's use 3 hours here,
35
/// it should be plenty to make it happen even if hiccups happen.
36
const KEY_ROTATION_EXPIRE_BUFFER: Duration = Duration::from_secs(3 * 60 * 60);
37

            
38
/// Key lifefime duration of 2 days
39
const KEY_DURATION_2DAYS: Duration = Duration::from_secs(2 * 24 * 60 * 60);
40
/// Key lifefime duration of 30 days
41
const KEY_DURATION_30DAYS: Duration = Duration::from_secs(30 * 24 * 60 * 60);
42
/// Key lifefime duration of 6 months
43
const KEY_DURATION_6MONTHS: Duration = Duration::from_secs(6 * 30 * 24 * 60 * 60);
44

            
45
/// Build a fresh [`RelayIdentities`] object using a [`KeyMgr`].
46
///
47
/// Every single certificate is generated in this function.
48
///
49
/// This function assumes that all required keys are in the keymgr.
50
4
fn build_proto_identities(now: SystemTime, keymgr: &KeyMgr) -> anyhow::Result<RelayIdentities> {
51
4
    let mut rng = tor_llcrypto::rng::CautiousRng;
52

            
53
    // Get the identity keypairs.
54
4
    let rsa_id_kp: RelayIdentityRsaKeypair = keymgr
55
4
        .get(&RelayIdentityRsaKeypairSpecifier::new())
56
4
        .context("Failed to get RSA identity from key manager")?
57
4
        .context("Missing RSA identity")?;
58
4
    let ed_id_kp: RelayIdentityKeypair = keymgr
59
4
        .get(&RelayIdentityKeypairSpecifier::new())
60
4
        .context("Failed to get Ed25519 identity from key manager")?
61
4
        .context("Missing Ed25519 identity")?;
62
    // We have to list match here because the key specifier here uses a valid_until. We don't know
63
    // what it is so we list and take the first one.
64
4
    let link_sign_kp: RelayLinkSigningKeypair = keymgr
65
4
        .get_entry(
66
4
            keymgr
67
4
                .list_matching(&RelayLinkSigningKeypairSpecifierPattern::new_any().arti_pattern()?)?
68
4
                .first()
69
4
                .context("No store entry for link authentication key")?,
70
        )
71
4
        .context("Failed to get link authentication key from key manager")?
72
4
        .context("Missing link authentication key")?;
73
4
    let kp_relaysign_id: RelaySigningKeypair = keymgr
74
4
        .get_entry(
75
4
            keymgr
76
4
                .list_matching(&RelaySigningKeypairSpecifierPattern::new_any().arti_pattern()?)?
77
4
                .first()
78
4
                .context("No store entry for signing key")?,
79
        )
80
4
        .context("Failed to get signing key from key manager")?
81
4
        .context("Missing signing key")?;
82

            
83
    // TLS key and cert. Random hostname like C-tor. We re-use the issuer_hostname for the RSA
84
    // legacy cert.
85
4
    let issuer_hostname = rand_hostname::random_hostname(&mut rng);
86
4
    let subject_hostname = rand_hostname::random_hostname(&mut rng);
87
4
    let tls_key_and_cert =
88
4
        TlsKeyAndCert::create(&mut rng, now, &issuer_hostname, &subject_hostname)
89
4
            .context("Failed to create TLS keys and certificates")?;
90

            
91
    // Create the RSA X509 certificate.
92
4
    let cert_id_x509_rsa = tor_cert::x509::create_legacy_rsa_id_cert(
93
4
        &mut rng,
94
4
        now,
95
4
        &issuer_hostname,
96
4
        rsa_id_kp.keypair(),
97
    )
98
4
    .context("Failed to create legacy RSA identity certificate")?;
99

            
100
    // The following expiry duration have been taken from C-tor.
101

            
102
4
    let cert_id_rsa = tor_cert::rsa::EncodedRsaCrosscert::encode_and_sign(
103
4
        rsa_id_kp.keypair(),
104
4
        &ed_id_kp.to_ed25519_id(),
105
4
        now + KEY_DURATION_6MONTHS,
106
    )?;
107

            
108
    // Create the signing key cert, link cert and tls cert.
109
    //
110
    // TODO(relay): We need to check the KeyMgr for the signing cert but for now the KeyMgr API
111
    // doesn't allow us to get it out. We will do a re-design of the cert API there. This is fine
112
    // as long as we don't support offline keys.
113
4
    let cert_id_sign_ed = gen_signing_cert(&ed_id_kp, &kp_relaysign_id, now + KEY_DURATION_30DAYS)?;
114
4
    let cert_sign_link_auth_ed =
115
4
        gen_link_cert(&kp_relaysign_id, &link_sign_kp, now + KEY_DURATION_2DAYS)?;
116
4
    let cert_sign_tls_ed = gen_tls_cert(
117
4
        &kp_relaysign_id,
118
4
        *tls_key_and_cert.link_cert_sha256(),
119
4
        now + KEY_DURATION_2DAYS,
120
    )?;
121

            
122
4
    Ok(RelayIdentities::new(
123
4
        &rsa_id_kp.public().into(),
124
4
        ed_id_kp.to_ed25519_id(),
125
4
        link_sign_kp,
126
4
        cert_id_sign_ed.to_encodable_cert(),
127
4
        cert_sign_tls_ed,
128
4
        cert_sign_link_auth_ed.to_encodable_cert(),
129
4
        cert_id_x509_rsa,
130
4
        cert_id_rsa,
131
4
        tls_key_and_cert,
132
4
    ))
133
4
}
134

            
135
/// Generate a key `K` directly into the key manager.
136
///
137
/// If the key already exists, the error is ignored as this could happen if the system time drifts
138
/// between the get and the generate.
139
40
fn generate_key<K>(keymgr: &KeyMgr, spec: &dyn KeySpecifier) -> Result<(), tor_keymgr::Error>
140
40
where
141
40
    K: ToEncodableKey,
142
40
    K::Key: Keygen,
143
{
144
40
    let mut rng = tor_llcrypto::rng::CautiousRng;
145

            
146
40
    match keymgr.generate::<K>(spec, KeystoreSelector::default(), &mut rng, false) {
147
40
        Ok(_) => {}
148
        // Key already existing can happen due to wall clock strangeness,
149
        // so simply ignore it.
150
        Err(tor_keymgr::Error::KeyAlreadyExists) => (),
151
        Err(e) => return Err(e),
152
    };
153
40
    Ok(())
154
40
}
155

            
156
/// Go through keystore entries matching `pattern` and remove any that are within
157
/// [`KEY_ROTATION_EXPIRE_BUFFER`] of expiry.
158
///
159
/// Returns `(removed, min_remaining)` where `removed` indicates whether any entry was deleted and
160
/// `min_remaining` is the minimum `valid_until` of the entries that were kept (if any).
161
120
fn remove_expired<F>(
162
120
    now: SystemTime,
163
120
    keymgr: &KeyMgr,
164
120
    pattern: &tor_keymgr::KeyPathPattern,
165
120
    label: &'static str,
166
120
    expiry_from_keypath: F,
167
120
) -> anyhow::Result<(bool, Option<SystemTime>)>
168
120
where
169
120
    F: Fn(&KeyPath) -> anyhow::Result<Timestamp>,
170
{
171
120
    let entries = keymgr.list_matching(pattern)?;
172
120
    let mut removed = false;
173
120
    let mut min_valid_until: Option<Timestamp> = None;
174

            
175
180
    for entry in entries {
176
60
        let valid_until = expiry_from_keypath(entry.key_path())?;
177
60
        if valid_until <= Timestamp::from(now + KEY_ROTATION_EXPIRE_BUFFER) {
178
20
            tracing::debug!("Expired {} in keymgr. Removing it.", label);
179
20
            keymgr.remove_entry(&entry)?;
180
20
            removed = true;
181
        } else {
182
            min_valid_until =
183
40
                Some(min_valid_until.map_or(valid_until, |current| current.min(valid_until)));
184
        }
185
    }
186

            
187
120
    Ok((removed, min_valid_until.map(SystemTime::from)))
188
120
}
189

            
190
/// Attempt to generate a key using the given [`KeySpecifier`].
191
///
192
/// Return true if generated else false.
193
40
fn try_generate_key<K, P>(keymgr: &KeyMgr, spec: &dyn KeySpecifier) -> anyhow::Result<bool>
194
40
where
195
40
    K: ToEncodableKey,
196
40
    K::Key: Keygen,
197
40
    P: KeySpecifierPattern,
198
{
199
40
    let mut generated = false;
200
40
    let mut rng = tor_llcrypto::rng::CautiousRng;
201
40
    let entries = keymgr.list_matching(&P::new_any().arti_pattern()?)?;
202
40
    if entries.is_empty() {
203
32
        let _ = keymgr.get_or_generate::<K>(spec, KeystoreSelector::default(), &mut rng)?;
204
32
        generated = true;
205
8
    }
206

            
207
40
    Ok(generated)
208
40
}
209

            
210
/// Attempt to generate a key and cert using the given [`KeyCertificateSpecifier`] which is signed
211
/// by the given [`KeySpecifier]` in `signing_key_spec`.
212
///
213
/// The `make_certificate` is used to generate the certificate stored in the [`KeyMgr`].
214
///
215
/// Return true if generated else false.
216
40
fn try_generate_key_cert<K, C, P>(
217
40
    keymgr: &KeyMgr,
218
40
    cert_spec: &dyn KeyCertificateSpecifier,
219
40
    signing_key_spec: &dyn KeySpecifier,
220
40
    make_certificate: impl FnOnce(&K, &<C as ToEncodableCert<K>>::SigningKey) -> C,
221
40
) -> anyhow::Result<bool>
222
40
where
223
40
    K: ToEncodableKey,
224
40
    K::Key: Keygen,
225
40
    C: ToEncodableCert<K>,
226
40
    P: CertSpecifierPattern,
227
{
228
40
    let mut generated = false;
229
40
    let mut rng = tor_llcrypto::rng::CautiousRng;
230
40
    let entries = keymgr.list_matching(&P::new_any().arti_pattern()?)?;
231
40
    if entries.is_empty() {
232
24
        let _ = keymgr.get_or_generate_key_and_cert::<K, C>(
233
24
            cert_spec,
234
24
            signing_key_spec,
235
24
            make_certificate,
236
24
            KeystoreSelector::default(),
237
24
            &mut rng,
238
        )?;
239
24
        generated = true;
240
16
    }
241

            
242
40
    Ok(generated)
243
40
}
244

            
245
/// Try to generate all keys and certs needed for a relay.
246
///
247
/// This tries to generate the [`RelayLinkSigningKeypair`] and the [`RelaySigningKeypair`] +
248
/// [`RelaySigningKeyCert`]. Note that identity keys are NOT generated within this function, it is
249
/// only attempted once at boot time. This is so we avoid retrying to generate them at each key
250
/// rotation as those identity keys never rotate.
251
///
252
/// Returns the minimum valid until value if a key was generated. Else, a None value indicates that
253
/// no key was generated.
254
40
fn try_generate_all(now: SystemTime, keymgr: &KeyMgr) -> anyhow::Result<Option<SystemTime>> {
255
40
    let link_expiry = now + KEY_DURATION_2DAYS;
256
40
    let link_spec = RelayLinkSigningKeypairSpecifier::new(Timestamp::from(link_expiry));
257
40
    let link_generated = try_generate_key::<
258
40
        RelayLinkSigningKeypair,
259
40
        RelayLinkSigningKeypairSpecifierPattern,
260
40
    >(keymgr, &link_spec)?;
261

            
262
    // The make certificate function needed for the get_or_generate_key_and_cert(). It is a closure
263
    // so we can capture the runtime wallclock.
264
40
    let make_signing_cert = |subject_key: &RelaySigningKeypair,
265
24
                             signing_key: &RelayIdentityKeypair| {
266
24
        gen_signing_cert(signing_key, subject_key, now + KEY_DURATION_30DAYS)
267
24
            .expect("failed to generate relay signing cert")
268
24
    };
269

            
270
40
    let cert_expiry = now + KEY_DURATION_30DAYS;
271
    // We either get the existing one or generate this new one.
272
40
    let cert_spec = RelaySigningKeyCertSpecifier::new(RelaySigningPublicKeySpecifier::new(
273
40
        Timestamp::from(cert_expiry),
274
    ));
275
40
    let cert_generated = try_generate_key_cert::<
276
40
        RelaySigningKeypair,
277
40
        RelaySigningKeyCert,
278
40
        RelaySigningKeyCertSpecifierPattern,
279
40
    >(
280
40
        keymgr,
281
40
        &cert_spec,
282
40
        &RelayIdentityKeypairSpecifier::new(),
283
40
        make_signing_cert,
284
    )?;
285

            
286
40
    Ok([
287
40
        link_generated.then_some(link_expiry),
288
40
        cert_generated.then_some(cert_expiry),
289
40
    ]
290
40
    .into_iter()
291
40
    .flatten()
292
40
    .min())
293
40
}
294

            
295
/// Remove any expired keys (and certs) that are expired.
296
///
297
/// Return (`removed`, `next_expiry`) where the `removed` indicates if at least one key has been
298
/// removed because it was expired. The `next_expiry` is the minimum value of all valid_until which
299
/// indicates the next closest expiry time.
300
40
fn remove_expired_keys(
301
40
    now: SystemTime,
302
40
    keymgr: &KeyMgr,
303
40
) -> anyhow::Result<(bool, Option<SystemTime>)> {
304
40
    let (relaysign_removed, relaysign_expiry) = remove_expired(
305
40
        now,
306
40
        keymgr,
307
40
        &RelaySigningKeypairSpecifierPattern::new_any().arti_pattern()?,
308
        "key KP_relaysign_ed",
309
20
        |key_path| Ok(RelaySigningKeypairSpecifier::try_from(key_path)?.valid_until()),
310
    )?;
311
40
    let (link_removed, link_expiry) = remove_expired(
312
40
        now,
313
40
        keymgr,
314
40
        &RelayLinkSigningKeypairSpecifierPattern::new_any().arti_pattern()?,
315
        "key KP_link_ed",
316
20
        |key_path| Ok(RelayLinkSigningKeypairSpecifier::try_from(key_path)?.valid_until()),
317
    )?;
318

            
319
    // This should always be removed if the signing key above has been removed. However, we still
320
    // do a pass at the keystore considering the upcoming offline key feature that might have more
321
    // than one expired cert in the keystore.
322
40
    let (sign_cert_removed, sign_cert_expiry) = remove_expired(
323
40
        now,
324
40
        keymgr,
325
40
        &RelaySigningKeyCertSpecifierPattern::new_any().arti_pattern()?,
326
        "signing key cert",
327
20
        |key_path| {
328
20
            let spec: RelaySigningKeyCertSpecifier = key_path.try_into()?;
329
20
            let subject_key_path = KeyPath::Arti(spec.subject_key_specifier().arti_path()?);
330
20
            let subject_key_spec: RelaySigningPublicKeySpecifier =
331
20
                (&subject_key_path).try_into()?;
332
20
            Ok(subject_key_spec.valid_until())
333
20
        },
334
    )?;
335

            
336
    // Have we at least removed one?
337
40
    let removed = relaysign_removed || link_removed || sign_cert_removed;
338

            
339
40
    let next_expiry = [relaysign_expiry, link_expiry, sign_cert_expiry]
340
40
        .into_iter()
341
40
        .flatten()
342
40
        .min();
343

            
344
40
    Ok((removed, next_expiry))
345
40
}
346

            
347
/// Attempt to rotate all keys except identity keys.
348
///
349
/// Returns (rotated, next_expiry) where `rotated` indicates if any key was rotated and
350
/// `next_expiry` is the earliest expiry time across all keys.
351
40
fn try_rotate_keys(now: SystemTime, keymgr: &KeyMgr) -> anyhow::Result<(bool, SystemTime)> {
352
    // First do a pass to remove every expired key(s) or/and cert(s).
353
40
    let (have_rotated, min_expiry) = remove_expired_keys(now, keymgr)?;
354

            
355
    // Then attempt to generate keys. If at least one was generated, we'll get the min expiry time
356
    // which we need to consider "rotated" so the caller can know that a new key appeared.
357
40
    let gen_min_expiry = try_generate_all(now, keymgr)?;
358
40
    let have_rotated = have_rotated || gen_min_expiry.is_some();
359

            
360
    // We should never get no expiry time.
361
40
    let next_expiry = [min_expiry, gen_min_expiry]
362
40
        .into_iter()
363
40
        .flatten()
364
40
        .min()
365
40
        .ok_or(internal!("No relay keys after rotation task loop"))?;
366

            
367
40
    Ok((have_rotated, next_expiry))
368
40
}
369

            
370
/// Attempt to generate all keys. The list of keys is:
371
///
372
/// * Identity Ed25519 keypair [`RelayIdentityKeypair`].
373
/// * Identity RSA [`RelayIdentityRsaKeypair`].
374
/// * Relay signing keypair [`RelaySigningKeypair`].
375
/// * Relay link signing keypair [`RelayLinkSigningKeypair`].
376
///
377
/// This function is only called when our relay bootstraps in order to attempt to generate any
378
/// missing keys or/and rotate expired keys.
379
4
pub(crate) fn try_generate_keys<R: Runtime>(
380
4
    runtime: &R,
381
4
    keymgr: &KeyMgr,
382
4
) -> anyhow::Result<RelayIdentities> {
383
4
    let now = runtime.wallclock();
384
    // Attempt to generate our identity keys (ed and RSA). Those keys DO NOT rotate. It won't be
385
    // replaced if they already exists.
386
4
    generate_key::<RelayIdentityKeypair>(keymgr, &RelayIdentityKeypairSpecifier::new())?;
387
4
    generate_key::<RelayIdentityRsaKeypair>(keymgr, &RelayIdentityRsaKeypairSpecifier::new())?;
388

            
389
    // Attempt to rotate the keys. Any missing keys (and cert) will be generated.
390
4
    let _ = try_rotate_keys(now, keymgr)?;
391

            
392
    // Now that we have our up-to-date keys, build the RelayIdentities object.
393
4
    build_proto_identities(now, keymgr)
394
4
}
395
/// Task to rotate keys when they need to be rotated.
396
pub(crate) async fn rotate_keys_task<R: Runtime>(
397
    runtime: R,
398
    keymgr: Arc<KeyMgr>,
399
    chanmgr: Arc<ChanMgr<R>>,
400
) -> anyhow::Result<void::Void> {
401
    loop {
402
        let now = runtime.wallclock();
403
        // Attempt a rotation of all keys.
404
        let (have_rotated, next_expiry) = try_rotate_keys(now, &keymgr)?;
405
        if have_rotated {
406
            let ids = build_proto_identities(now, &keymgr)?;
407
            chanmgr
408
                .set_relay_identities(Arc::new(ids))
409
                .context("Failed to set relay identities on ChanMgr")?;
410
        }
411

            
412
        // Sleep until the earliest key expiry minus buffer so we rotate before it expires.
413
        // If the subtraction would underflow, wake up immediately to rotate the expired key.
414
        let next_wake = next_expiry
415
            .checked_sub(KEY_ROTATION_EXPIRE_BUFFER)
416
            .unwrap_or(now);
417
        runtime.sleep_until_wallclock(next_wake).await;
418
    }
419
}
420

            
421
#[cfg(test)]
422
mod test {
423
    // @@ begin test lint list maintained by maint/add_warning @@
424
    #![allow(clippy::bool_assert_comparison)]
425
    #![allow(clippy::clone_on_copy)]
426
    #![allow(clippy::dbg_macro)]
427
    #![allow(clippy::mixed_attributes_style)]
428
    #![allow(clippy::print_stderr)]
429
    #![allow(clippy::print_stdout)]
430
    #![allow(clippy::single_char_pattern)]
431
    #![allow(clippy::unwrap_used)]
432
    #![allow(clippy::unchecked_time_subtraction)]
433
    #![allow(clippy::useless_vec)]
434
    #![allow(clippy::needless_pass_by_value)]
435
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
436

            
437
    use super::*;
438

            
439
    use tor_keymgr::{ArtiEphemeralKeystore, KeyMgrBuilder};
440
    use tor_relay_crypto::pk::{
441
        RelayLinkSigningKeypairSpecifierPattern, RelaySigningKeypairSpecifierPattern,
442
    };
443
    use tor_rtcompat::SleepProvider;
444
    use tor_rtmock::MockRuntime;
445

            
446
    /// Generate the non-rotating identity keys so the rest of the key machinery can run.
447
    fn setup_identity_keys(keymgr: &KeyMgr) {
448
        use tor_relay_crypto::pk::{
449
            RelayIdentityKeypair, RelayIdentityKeypairSpecifier, RelayIdentityRsaKeypair,
450
            RelayIdentityRsaKeypairSpecifier,
451
        };
452
        generate_key::<RelayIdentityKeypair>(keymgr, &RelayIdentityKeypairSpecifier::new())
453
            .unwrap();
454
        generate_key::<RelayIdentityRsaKeypair>(keymgr, &RelayIdentityRsaKeypairSpecifier::new())
455
            .unwrap();
456
    }
457

            
458
    /// Initialize test basics that is runtime and a KeyMgr.
459
    fn new_keymgr() -> KeyMgr {
460
        let store = Box::new(ArtiEphemeralKeystore::new("test".to_string()));
461
        KeyMgrBuilder::default()
462
            .primary_store(store)
463
            .build()
464
            .unwrap()
465
    }
466

            
467
    /// Initial setup of a test. Build a mock runtime, key manager and setup identity keys.
468
    fn setup() -> KeyMgr {
469
        let keymgr = new_keymgr();
470
        setup_identity_keys(&keymgr);
471
        keymgr
472
    }
473

            
474
    /// Return a [`Timestamp`] given a [`SystemTime`] rounded down to its nearest second.
475
    ///
476
    /// In other words, the `tv_nsec` of a [`SystemTime`] is dropped.
477
    fn to_timestamp_in_secs(valid_until: SystemTime) -> Timestamp {
478
        use std::time::UNIX_EPOCH;
479
        let seconds = valid_until.duration_since(UNIX_EPOCH).unwrap().as_secs();
480
        Timestamp::from(UNIX_EPOCH + Duration::from_secs(seconds))
481
    }
482

            
483
    /// Return the number of link keys in the given KeyMgr.
484
    fn count_link_keys(keymgr: &KeyMgr) -> usize {
485
        keymgr
486
            .list_matching(
487
                &RelayLinkSigningKeypairSpecifierPattern::new_any()
488
                    .arti_pattern()
489
                    .unwrap(),
490
            )
491
            .unwrap()
492
            .len()
493
    }
494

            
495
    /// Return the number of signing keys in the given KeyMgr.
496
    fn count_signing_keys(keymgr: &KeyMgr) -> usize {
497
        keymgr
498
            .list_matching(
499
                &RelaySigningKeypairSpecifierPattern::new_any()
500
                    .arti_pattern()
501
                    .unwrap(),
502
            )
503
            .unwrap()
504
            .len()
505
    }
506

            
507
    /// Test the actual bootstrap function, `try_generate_keys()` which is in charge of
508
    /// initializing the identities.
509
    #[test]
510
    fn test_bootstrap() {
511
        MockRuntime::test_with_various(|runtime| async move {
512
            let keymgr = new_keymgr();
513

            
514
            let _identities = match try_generate_keys(&runtime, &keymgr) {
515
                Ok(ident) => ident,
516
                Err(e) => {
517
                    panic!("Unable to bootstrap keys and generate RelayIdentities: {e}");
518
                }
519
            };
520
        });
521
    }
522

            
523
    /// Simulate the bootstrap when no keys exists. We should have one link key and one signing key
524
    /// after the first rotation.
525
    #[test]
526
    fn test_initial_key_generation() {
527
        MockRuntime::test_with_various(|runtime| async move {
528
            let keymgr = setup();
529
            let now = runtime.wallclock();
530

            
531
            let (rotated, next_expiry) = try_rotate_keys(now, &keymgr).unwrap();
532

            
533
            assert!(
534
                rotated,
535
                "keys should be reported as generated on first rotation"
536
            );
537
            assert_eq!(count_link_keys(&keymgr), 1, "expected one link key");
538
            assert_eq!(count_signing_keys(&keymgr), 1, "expected one signing key");
539

            
540
            // The earliest expiry should be the link key (~2 days out).
541
            let expected = runtime.wallclock() + KEY_DURATION_2DAYS;
542
            assert_eq!(
543
                next_expiry, expected,
544
                "next expiry should be ~{KEY_DURATION_2DAYS:?} from now, got {next_expiry:?}"
545
            );
546
        });
547
    }
548

            
549
    /// Calling rotate_keys a second time with fresh keys should indicate no rotation.
550
    #[test]
551
    fn test_rotation_on_fresh_keys() {
552
        MockRuntime::test_with_various(|runtime| async move {
553
            let keymgr = setup();
554
            let now = runtime.wallclock();
555
            try_rotate_keys(now, &keymgr).unwrap();
556

            
557
            // Advance by 1 hour (inside 2 days of link key).
558
            runtime.advance_by(Duration::from_secs(60 * 60)).await;
559

            
560
            let (rotated, _) = try_rotate_keys(now, &keymgr).unwrap();
561

            
562
            assert!(!rotated, "fresh keys must not trigger a rotation");
563
            assert_eq!(count_link_keys(&keymgr), 1, "expected one link key");
564
            assert_eq!(count_signing_keys(&keymgr), 1, "expected one signing key");
565
        });
566
    }
567

            
568
    /// Test rotation before and after rotation expiry buffer for the link key.
569
    #[test]
570
    fn test_rotation_link_key() {
571
        MockRuntime::test_with_various(|runtime| async move {
572
            let keymgr = setup();
573
            // First rotation creates the keys.
574
            try_rotate_keys(runtime.wallclock(), &keymgr).unwrap();
575

            
576
            // Advance to 1 second _before_ the rotation-buffer threshold. We should not rotate
577
            // with this.
578
            let just_before =
579
                KEY_DURATION_2DAYS - KEY_ROTATION_EXPIRE_BUFFER - Duration::from_secs(1);
580
            runtime.advance_by(just_before).await;
581

            
582
            let (rotated, _) = try_rotate_keys(runtime.wallclock(), &keymgr).unwrap();
583

            
584
            assert!(
585
                !rotated,
586
                "link key MUST NOT rotate before the expiry buffer threshold"
587
            );
588
            assert_eq!(count_link_keys(&keymgr), 1, "expected one link key");
589
            assert_eq!(count_signing_keys(&keymgr), 1, "expected one signing key");
590

            
591
            // Move it just after the expiry buffer and expect a rotation.
592
            let just_after =
593
                KEY_DURATION_2DAYS - KEY_ROTATION_EXPIRE_BUFFER + Duration::from_secs(1);
594
            runtime.advance_by(just_after).await;
595

            
596
            let (rotated, _) = try_rotate_keys(runtime.wallclock(), &keymgr).unwrap();
597
            assert!(
598
                rotated,
599
                "link key should rotate inside the expiry buffer threshold"
600
            );
601
        });
602
    }
603

            
604
    /// Test rotation before and after rotation expiry buffer for the signing key.
605
    #[test]
606
    fn test_rotation_signing_key() {
607
        MockRuntime::test_with_various(|runtime| async move {
608
            let keymgr = setup();
609
            // First rotation creates the keys.
610
            try_rotate_keys(runtime.wallclock(), &keymgr).unwrap();
611

            
612
            // Closure to get the relay signing key keystore entry.
613
            let get_key_spec = || {
614
                let entries = keymgr
615
                    .list_matching(
616
                        &RelaySigningKeypairSpecifierPattern::new_any()
617
                            .arti_pattern()
618
                            .unwrap(),
619
                    )
620
                    .unwrap();
621
                let entry = entries.first().unwrap();
622
                let spec: RelaySigningKeypairSpecifier = entry.key_path().try_into().unwrap();
623
                spec
624
            };
625

            
626
            // Advance to 1 second _before_ the rotation-buffer threshold. We should not rotate
627
            // with this.
628
            let just_before =
629
                KEY_DURATION_30DAYS - KEY_ROTATION_EXPIRE_BUFFER - Duration::from_secs(1);
630
            runtime.advance_by(just_before).await;
631

            
632
            let (rotated, _) = try_rotate_keys(runtime.wallclock(), &keymgr).unwrap();
633
            assert!(rotated, "Rotation must happen after 30 days");
634

            
635
            let spec = get_key_spec();
636
            assert_eq!(
637
                spec.valid_until(),
638
                to_timestamp_in_secs(
639
                    runtime.wallclock() + KEY_ROTATION_EXPIRE_BUFFER + Duration::from_secs(1)
640
                ),
641
                "RelaySigningKeypairSpecifier should not have rotated"
642
            );
643

            
644
            assert_eq!(count_link_keys(&keymgr), 1, "expected one link key");
645
            assert_eq!(count_signing_keys(&keymgr), 1, "expected one signing key");
646

            
647
            // Move it just after the expiry buffer and expect a rotation.
648
            let just_after =
649
                KEY_DURATION_30DAYS - KEY_ROTATION_EXPIRE_BUFFER + Duration::from_secs(1);
650
            runtime.advance_by(just_after).await;
651

            
652
            let (rotated, _) = try_rotate_keys(runtime.wallclock(), &keymgr).unwrap();
653
            assert!(rotated, "Rotation must happen after 30 days");
654

            
655
            let spec = get_key_spec();
656
            assert_eq!(
657
                spec.valid_until(),
658
                to_timestamp_in_secs(runtime.wallclock() + KEY_DURATION_30DAYS),
659
                "RelaySigningKeypairSpecifier should have rotated"
660
            );
661
        });
662
    }
663
}