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

            
3
use anyhow::Context;
4
use std::borrow::Borrow;
5
use std::time::{Duration, SystemTime};
6

            
7
use tor_basic_utils::rand_hostname;
8
use tor_cert::x509::TlsKeyAndCert;
9
use tor_error::internal;
10
use tor_key_forge::ToEncodableCert;
11
use tor_keymgr::{
12
    CertSpecifierPattern, KeyCertificateSpecifier, KeyMgr, KeyPath, KeySpecifier,
13
    KeySpecifierPattern, Keygen, KeystoreEntry, KeystoreSelector, ToEncodableKey,
14
};
15
use tor_proto::RelayChannelAuthMaterial;
16
use tor_relay_crypto::pk::{
17
    RelayIdentityKeypair, RelayLinkSigningKeypair, RelayNtorKeypair, RelaySigningKeypair,
18
};
19
use tor_relay_crypto::{RelaySigningKeyCert, gen_link_cert, gen_signing_cert, gen_tls_cert};
20

            
21
use crate::{
22
    keys::{
23
        RelayIdentityKeypairSpecifier, RelayLinkSigningKeypairSpecifier,
24
        RelayLinkSigningKeypairSpecifierPattern, RelayNtorKeypairSpecifier,
25
        RelayNtorKeypairSpecifierPattern, RelaySigningKeyCertSpecifier,
26
        RelaySigningKeyCertSpecifierPattern, RelaySigningKeypairSpecifier,
27
        RelaySigningKeypairSpecifierPattern, RelaySigningPublicKeySpecifier, Timestamp,
28
    },
29
    tasks::crypto::{KeyRotationParams, views::FullKeyView},
30
};
31

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

            
39
// The following expiry durations have been taken from C-tor.
40

            
41
/// Lifetime of the link authentication key (KP_link_ed) certificate.
42
const LINK_CERT_LIFETIME: Duration = Duration::from_secs(2 * 24 * 60 * 60);
43
/// Lifetime of the relay signing key (KP_relaysign_ed) certificate.
44
const SIGNING_KEY_CERT_LIFETIME: Duration = Duration::from_secs(30 * 24 * 60 * 60);
45
/// Lifetime of the RSA identity key certificate.
46
const RSA_CROSSCERT_LIFETIME: Duration = Duration::from_secs(6 * 30 * 24 * 60 * 60);
47

            
48
/// Build a fresh [`RelayChannelAuthMaterial`] object using a [`KeyMgr`].
49
///
50
/// The link cert and TLS certs are created in this function.
51
/// The signing key certificate is retrieved from the keymgr.
52
///
53
/// This function assumes that all required keys,
54
/// as well as the signing key certificate,
55
/// are already in the keystore.
56
2
pub(super) fn build_proto_relay_auth_material(
57
2
    now: SystemTime,
58
2
    view: &FullKeyView<impl Borrow<KeyMgr>>,
59
2
) -> anyhow::Result<RelayChannelAuthMaterial> {
60
2
    let mut rng = tor_llcrypto::rng::CautiousRng;
61

            
62
2
    let rsa_id_kp = view.ks_relayid_rsa()?;
63
2
    let ed_id_kp = view.ks_relayid_ed()?;
64
2
    let link_sign_kp = view.ks_link_ed()?;
65
2
    let kp_relaysign_id = view.ks_relaysign_ed()?;
66
2
    let cert_id_sign_ed = view.cert_relaysign_ed()?;
67

            
68
    // TLS key and cert. Random hostname like C-tor. We re-use the issuer_hostname for the RSA
69
    // legacy cert.
70
2
    let issuer_hostname = rand_hostname::random_hostname(&mut rng);
71
2
    let subject_hostname = rand_hostname::random_hostname(&mut rng);
72
2
    let tls_key_and_cert =
73
2
        TlsKeyAndCert::create(&mut rng, now, &issuer_hostname, &subject_hostname)
74
2
            .context("Failed to create TLS keys and certificates")?;
75

            
76
    // Create the RSA X509 certificate.
77
2
    let cert_id_x509_rsa = tor_cert::x509::create_legacy_rsa_id_cert(
78
2
        &mut rng,
79
2
        now,
80
2
        &issuer_hostname,
81
2
        rsa_id_kp.keypair(),
82
    )
83
2
    .context("Failed to create legacy RSA identity certificate")?;
84

            
85
2
    let cert_id_rsa = tor_cert::rsa::EncodedRsaCrosscert::encode_and_sign(
86
2
        rsa_id_kp.keypair(),
87
2
        &ed_id_kp.to_ed25519_id(),
88
2
        now + RSA_CROSSCERT_LIFETIME,
89
    )?;
90

            
91
    // Create the link cert and tls cert.
92
2
    let cert_sign_link_auth_ed =
93
2
        gen_link_cert(&kp_relaysign_id, &link_sign_kp, now + LINK_CERT_LIFETIME)?;
94
2
    let cert_sign_tls_ed = gen_tls_cert(
95
2
        &kp_relaysign_id,
96
2
        *tls_key_and_cert.link_cert_sha256(),
97
2
        now + LINK_CERT_LIFETIME,
98
    )?;
99

            
100
2
    Ok(RelayChannelAuthMaterial::new(
101
2
        &rsa_id_kp.public().into(),
102
2
        ed_id_kp.to_ed25519_id(),
103
2
        link_sign_kp,
104
2
        cert_id_sign_ed.to_encodable_cert(),
105
2
        cert_sign_tls_ed,
106
2
        cert_sign_link_auth_ed.to_encodable_cert(),
107
2
        cert_id_x509_rsa,
108
2
        cert_id_rsa,
109
2
        tls_key_and_cert,
110
2
    ))
111
2
}
112

            
113
/// Generate a key `K` directly into the key manager.
114
///
115
/// If the key already exists, the error is ignored as this could happen if the system time drifts
116
/// between the get and the generate.
117
34
pub(super) fn generate_key<K>(
118
34
    keymgr: &KeyMgr,
119
34
    spec: &dyn KeySpecifier,
120
34
) -> Result<(), tor_keymgr::Error>
121
34
where
122
34
    K: ToEncodableKey,
123
34
    K::Key: Keygen,
124
{
125
34
    let mut rng = tor_llcrypto::rng::CautiousRng;
126

            
127
34
    match keymgr.generate::<K>(spec, KeystoreSelector::default(), &mut rng, false) {
128
34
        Ok(_) => {}
129
        // Key already existing can happen due to wall clock strangeness,
130
        // so simply ignore it.
131
        Err(tor_keymgr::Error::KeyAlreadyExists) => (),
132
        Err(e) => return Err(e),
133
    };
134
34
    Ok(())
135
34
}
136

            
137
/// Go through keystore entries matching `pattern` and remove any that are expired according to
138
/// `is_expired`.
139
///
140
/// Returns `min_remaining` which is the minimum `valid_until` of the entries that were kept (if
141
/// any).
142
112
fn remove_expired<F, E>(
143
112
    now: SystemTime,
144
112
    keymgr: &KeyMgr,
145
112
    pattern: &tor_keymgr::KeyPathPattern,
146
112
    label: &'static str,
147
112
    expiry_from_keypath: F,
148
112
    is_expired: E,
149
112
) -> anyhow::Result<Option<SystemTime>>
150
112
where
151
112
    F: Fn(&KeyPath) -> anyhow::Result<Timestamp>,
152
112
    E: Fn(&Timestamp, SystemTime) -> bool,
153
{
154
112
    let entries = keymgr.list_matching(pattern)?;
155
112
    let mut min_valid_until: Option<Timestamp> = None;
156

            
157
112
    for entry in entries {
158
68
        let valid_until = expiry_from_keypath(entry.key_path())?;
159
68
        if is_expired(&valid_until, now) {
160
18
            tracing::debug!("Expired {} in keymgr. Removing it.", label);
161
18
            keymgr.remove_entry(&entry)?;
162
        } else {
163
            min_valid_until =
164
50
                Some(min_valid_until.map_or(valid_until, |current| current.min(valid_until)));
165
        }
166
    }
167

            
168
112
    Ok(min_valid_until.map(SystemTime::from))
169
112
}
170

            
171
/// Attempt to generate a key using the given [`KeySpecifier`].
172
///
173
/// Return true if generated else false.
174
56
fn try_generate_key<K, P, F>(
175
56
    keymgr: &KeyMgr,
176
56
    spec: &dyn KeySpecifier,
177
56
    should_generate: F,
178
56
) -> anyhow::Result<bool>
179
56
where
180
56
    K: ToEncodableKey,
181
56
    K::Key: Keygen,
182
56
    P: KeySpecifierPattern,
183
56
    F: Fn(&[KeystoreEntry]) -> anyhow::Result<bool>,
184
{
185
56
    let mut generated = false;
186
56
    let mut rng = tor_llcrypto::rng::CautiousRng;
187
56
    let entries = keymgr.list_matching(&P::new_any().arti_pattern()?)?;
188
56
    if should_generate(&entries)? {
189
36
        let _ = keymgr.get_or_generate::<K>(spec, KeystoreSelector::default(), &mut rng)?;
190
36
        generated = true;
191
20
    }
192

            
193
56
    Ok(generated)
194
56
}
195

            
196
/// Attempt to generate a key and cert using the given [`KeyCertificateSpecifier`] which is signed
197
/// by the given [`KeySpecifier]` in `signing_key_spec`.
198
///
199
/// The `make_certificate` is used to generate the certificate stored in the [`KeyMgr`].
200
///
201
/// Return true if generated else false.
202
28
fn try_generate_key_cert<K, C, P>(
203
28
    keymgr: &KeyMgr,
204
28
    cert_spec: &dyn KeyCertificateSpecifier,
205
28
    signing_key_spec: &dyn KeySpecifier,
206
28
    make_certificate: impl FnOnce(&K, &<C as ToEncodableCert<K>>::SigningKey) -> C,
207
28
) -> anyhow::Result<bool>
208
28
where
209
28
    K: ToEncodableKey,
210
28
    K::Key: Keygen,
211
28
    C: ToEncodableCert<K>,
212
28
    P: CertSpecifierPattern,
213
{
214
28
    let mut generated = false;
215
28
    let mut rng = tor_llcrypto::rng::CautiousRng;
216
28
    let entries = keymgr.list_matching(&P::new_any().arti_pattern()?)?;
217
28
    if entries.is_empty() {
218
16
        let _ = keymgr.get_or_generate_key_and_cert::<K, C>(
219
16
            cert_spec,
220
16
            signing_key_spec,
221
16
            make_certificate,
222
16
            KeystoreSelector::default(),
223
16
            &mut rng,
224
        )?;
225
16
        generated = true;
226
12
    }
227

            
228
28
    Ok(generated)
229
28
}
230

            
231
/// Try to generate all keys and certs needed for a relay.
232
///
233
/// This tries to generate the [`RelayLinkSigningKeypair`] and the [`RelaySigningKeypair`] +
234
/// [`RelaySigningKeyCert`]. Note that identity keys are NOT generated within this function, it is
235
/// only attempted once at boot time. This is so we avoid retrying to generate them at each key
236
/// rotation as those identity keys never rotate.
237
///
238
/// Returns the minimum `valid_until` across newly generated keys, or `None` if nothing was generated.
239
28
fn try_generate_all(
240
28
    now: SystemTime,
241
28
    keymgr: &KeyMgr,
242
28
    params: KeyRotationParams,
243
28
) -> anyhow::Result<Option<SystemTime>> {
244
28
    let link_expiry = now + LINK_CERT_LIFETIME;
245
28
    let link_spec = RelayLinkSigningKeypairSpecifier::new(Timestamp::from(link_expiry));
246
28
    let link_generated =
247
28
        try_generate_key::<RelayLinkSigningKeypair, RelayLinkSigningKeypairSpecifierPattern, _>(
248
28
            keymgr,
249
28
            &link_spec,
250
28
            |entries: &[KeystoreEntry<'_>]| Ok(entries.is_empty()),
251
        )?;
252

            
253
28
    let cert_expiry = now + SIGNING_KEY_CERT_LIFETIME;
254

            
255
    // The make certificate function needed for the get_or_generate_key_and_cert(). It is a closure
256
    // so we can capture the runtime wallclock.
257
28
    let make_signing_cert = |subject_key: &RelaySigningKeypair,
258
16
                             signing_key: &RelayIdentityKeypair| {
259
16
        gen_signing_cert(signing_key, subject_key, cert_expiry)
260
16
            .expect("failed to generate relay signing cert")
261
16
    };
262

            
263
    // We either get the existing one or generate this new one.
264
28
    let cert_spec = RelaySigningKeyCertSpecifier::new(RelaySigningPublicKeySpecifier::new(
265
28
        Timestamp::from(cert_expiry),
266
    ));
267
28
    let cert_generated = try_generate_key_cert::<
268
28
        RelaySigningKeypair,
269
28
        RelaySigningKeyCert,
270
28
        RelaySigningKeyCertSpecifierPattern,
271
28
    >(
272
28
        keymgr,
273
28
        &cert_spec,
274
28
        &RelayIdentityKeypairSpecifier::new(),
275
28
        make_signing_cert,
276
    )?;
277

            
278
28
    let ntor_expiry = now + params.ntor_lifetime;
279
28
    let ntor_spec = RelayNtorKeypairSpecifier::new(Timestamp::from(ntor_expiry));
280

            
281
    // We generate a new ntor key if all existing keys are expired `now`
282
    // (without taking into account the grace period)
283
28
    let should_generate_ntor = |entries: &[KeystoreEntry<'_>]| {
284
28
        let mut all_expired = true;
285
28
        for entry in entries {
286
17
            let key_path = entry.key_path();
287
17
            let valid_until =
288
17
                SystemTime::from(RelayNtorKeypairSpecifier::try_from(key_path)?.valid_until);
289

            
290
            // If *all* the ntor keys are expired (but still within the grace period),
291
            // we want to generate a new ntor key.
292
            //
293
            // Note: this needs to take the KEY_ROTATION_EXPIRE_BUFFER into account
294
            // because the main loop will wake us KEY_ROTATION_EXPIRE_BUFFER
295
            // *before* the valid_until elapses
296
17
            if valid_until > now + KEY_ROTATION_EXPIRE_BUFFER {
297
12
                all_expired = false;
298
12
                break;
299
5
            }
300
        }
301

            
302
28
        Ok(all_expired)
303
28
    };
304

            
305
28
    let ntor_generated = try_generate_key::<RelayNtorKeypair, RelayNtorKeypairSpecifierPattern, _>(
306
28
        keymgr,
307
28
        &ntor_spec,
308
28
        should_generate_ntor,
309
    )?;
310

            
311
28
    Ok([
312
28
        link_generated.then_some(link_expiry),
313
28
        cert_generated.then_some(cert_expiry),
314
28
        ntor_generated.then_some(ntor_expiry),
315
28
    ]
316
28
    .into_iter()
317
28
    .flatten()
318
28
    .min())
319
28
}
320

            
321
/// Remove any expired keys (and certs) that are expired.
322
///
323
/// Return (`removed`, `next_expiry`) where the `removed` indicates if at least one key has been
324
/// removed because it was expired. The `next_expiry` is the minimum value of all valid_until which
325
/// indicates the next closest expiry time.
326
28
fn remove_expired_keys(
327
28
    now: SystemTime,
328
28
    keymgr: &KeyMgr,
329
28
    params: KeyRotationParams,
330
28
) -> anyhow::Result<Option<SystemTime>> {
331
48
    let is_expired_with_buffer = |valid_until: &Timestamp, now| {
332
48
        *valid_until <= Timestamp::from(now + KEY_ROTATION_EXPIRE_BUFFER)
333
48
    };
334
28
    let relaysign_expiry = remove_expired(
335
28
        now,
336
28
        keymgr,
337
28
        &RelaySigningKeypairSpecifierPattern::new_any().arti_pattern()?,
338
        "key KP_relaysign_ed",
339
16
        |key_path| Ok(RelaySigningKeypairSpecifier::try_from(key_path)?.valid_until),
340
28
        is_expired_with_buffer,
341
    )?;
342
28
    let link_expiry = remove_expired(
343
28
        now,
344
28
        keymgr,
345
28
        &RelayLinkSigningKeypairSpecifierPattern::new_any().arti_pattern()?,
346
        "key KP_link_ed",
347
16
        |key_path| Ok(RelayLinkSigningKeypairSpecifier::try_from(key_path)?.valid_until),
348
28
        is_expired_with_buffer,
349
    )?;
350

            
351
    // This should always be removed if the signing key above has been removed. However, we still
352
    // do a pass at the keystore considering the upcoming offline key feature that might have more
353
    // than one expired cert in the keystore.
354
28
    let sign_cert_expiry = remove_expired(
355
28
        now,
356
28
        keymgr,
357
28
        &RelaySigningKeyCertSpecifierPattern::new_any().arti_pattern()?,
358
        "signing key cert",
359
16
        |key_path| {
360
16
            let spec: RelaySigningKeyCertSpecifier = key_path.try_into()?;
361
16
            let subject_key_path = KeyPath::Arti(spec.subject_key_specifier().arti_path()?);
362
16
            let subject_key_spec: RelaySigningPublicKeySpecifier =
363
16
                (&subject_key_path).try_into()?;
364
16
            Ok(subject_key_spec.valid_until)
365
16
        },
366
28
        is_expired_with_buffer,
367
    )?;
368

            
369
    // When deciding whether to remove the key,
370
    // we need to take into account the special grace period ntor keys have
371
    // (we need to keep the key around even if it's "expired",
372
    // because some clients might still be using an older consensus
373
    // and hence might not know about our new key yet).
374
28
    let is_expired_ntor = |valid_until: &Timestamp, now| {
375
        // Note: we need to take into account KEY_ROTATION_EXPIRE_BUFFER
376
        // because the main loop always subtracts KEY_ROTATION_EXPIRE_BUFFER
377
        // from the returned next_expiry, but ideally,
378
        // I don't think we should be using this buffer for the ntor keys,
379
        // because they have a grace period and don't get removed immediately
380
        // anyway
381
20
        *valid_until <= Timestamp::from(now - params.ntor_grace_period + KEY_ROTATION_EXPIRE_BUFFER)
382
20
    };
383

            
384
28
    let ntor_key_expiry = remove_expired(
385
28
        now,
386
28
        keymgr,
387
28
        &RelayNtorKeypairSpecifierPattern::new_any().arti_pattern()?,
388
        "key KP_ntor",
389
20
        |key_path| Ok(RelayNtorKeypairSpecifier::try_from(key_path)?.valid_until),
390
28
        is_expired_ntor,
391
    )?;
392

            
393
    // TODO: we could, in theory, return this from remove_expired(),
394
    // but I don't want to make it any more complicated than it already is,
395
    // especially for an operation that runs relatively infrequently.
396
28
    let ntor_key_count = keymgr
397
28
        .list_matching(&RelayNtorKeypairSpecifierPattern::new_any().arti_pattern()?)?
398
28
        .len();
399

            
400
    // This is a best effort check. There is no guarantee the
401
    // second key is the "successor" of this key,
402
    // but in general, it will be, unless an external process
403
    // is concurrently modifying the keystore
404
    // (which something we explicitly don't try to protect against).
405
    //
406
    // We could, in theory, check that the valid_until of the two
407
    // keys are adequately spaced, but in practice I don't think
408
    // it matters much.
409
28
    let next_key_exists = ntor_key_count >= 2;
410

            
411
    // Note: for each ntor key, we need to wake up twice
412
    //
413
    //   * at its expiry time, to generate the next ntor key
414
    //   * at its expiry time + GRACE_PERIOD, to remove the old ntor key
415
28
    let ntor_key_expiry = match ntor_key_expiry {
416
        None => {
417
            // We removed the last ntor key, the wakeup time will be
418
            // determined by try_generate_key() later
419
12
            None
420
        }
421
        // This special case may seem strange, but it's needed for
422
        // the specific scenario where there is only one ntor key
423
        // in the keystore with valid_until < now.
424
        //
425
        // Without it, there is no guarantee we will wake up at valid_until
426
        // to generate the new ntor key (when the key is generated,
427
        // we try to schedule a rotation task wakeup at valid_until,
428
        // but if the other keys have "sooner" `valid_until`s,
429
        // that wakeup will be lost.
430
14
        Some(valid_until) if !next_key_exists => {
431
            // The next key doesn't exist yet,
432
            // wake up at valid_until to generate it
433
14
            Some(valid_until)
434
        }
435
2
        Some(valid_until) => {
436
            // The next key exists, we only need to wake up
437
            // to garbage collect this one, after the grace period
438
            //
439
            // This avoids busy looping in the [valid_until, valid_until + grace_period]
440
            // time interval (if we don't add the grace period here, when
441
            // now = valid_until, we will keep waking up the main loop of the
442
            // key rotation task, and then not actually removing the key because
443
            // it's still within the grace period).
444
2
            Some(valid_until + params.ntor_grace_period)
445
        }
446
    };
447

            
448
28
    let next_expiry = [
449
28
        relaysign_expiry,
450
28
        link_expiry,
451
28
        sign_cert_expiry,
452
28
        ntor_key_expiry,
453
28
    ]
454
28
    .into_iter()
455
28
    .flatten()
456
28
    .min();
457

            
458
28
    Ok(next_expiry)
459
28
}
460

            
461
/// Attempt to rotate all keys except identity keys.
462
///
463
/// Returns the earliest expiry time across all keys.
464
28
pub(super) fn try_rotate_keys(
465
28
    now: SystemTime,
466
28
    keymgr: &KeyMgr,
467
28
    params: KeyRotationParams,
468
28
) -> anyhow::Result<SystemTime> {
469
28
    let min_expiry = remove_expired_keys(now, keymgr, params)?;
470
    // Then attempt to generate keys. If at least one was generated, we'll get the min expiry time
471
    // which we need to consider "rotated" so the caller can know that a new key appeared.
472
28
    let gen_min_expiry = try_generate_all(now, keymgr, params)?;
473

            
474
    // We should never get no expiry time.
475
28
    Ok([min_expiry, gen_min_expiry]
476
28
        .into_iter()
477
28
        .flatten()
478
28
        .min()
479
28
        .ok_or_else(|| internal!("No relay keys after rotation task loop"))?)
480
28
}
481

            
482
#[cfg(test)]
483
mod test {
484
    // @@ begin test lint list maintained by maint/add_warning @@
485
    #![allow(clippy::bool_assert_comparison)]
486
    #![allow(clippy::clone_on_copy)]
487
    #![allow(clippy::dbg_macro)]
488
    #![allow(clippy::mixed_attributes_style)]
489
    #![allow(clippy::print_stderr)]
490
    #![allow(clippy::print_stdout)]
491
    #![allow(clippy::single_char_pattern)]
492
    #![allow(clippy::unwrap_used)]
493
    #![allow(clippy::unchecked_time_subtraction)]
494
    #![allow(clippy::useless_vec)]
495
    #![allow(clippy::needless_pass_by_value)]
496
    #![allow(clippy::string_slice)] // See arti#2571
497
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
498

            
499
    use super::*;
500

            
501
    use crate::{
502
        keys::{RelayLinkSigningKeypairSpecifierPattern, RelaySigningKeypairSpecifierPattern},
503
        tasks::crypto::test::new_keymgr,
504
    };
505
    use tor_keymgr::KeySpecifierPattern;
506
    use tor_rtcompat::SleepProvider;
507
    use tor_rtmock::MockRuntime;
508

            
509
    /// Generate the non-rotating identity keys so the rest of the key machinery can run.
510
    fn setup_identity_keys(keymgr: &KeyMgr) {
511
        use crate::keys::{RelayIdentityKeypairSpecifier, RelayIdentityRsaKeypairSpecifier};
512
        use tor_relay_crypto::pk::{RelayIdentityKeypair, RelayIdentityRsaKeypair};
513
        generate_key::<RelayIdentityKeypair>(keymgr, &RelayIdentityKeypairSpecifier::new())
514
            .unwrap();
515
        generate_key::<RelayIdentityRsaKeypair>(keymgr, &RelayIdentityRsaKeypairSpecifier::new())
516
            .unwrap();
517
    }
518

            
519
    /// Initial setup of a test. Build a mock runtime, key manager and setup identity keys.
520
    fn setup() -> KeyMgr {
521
        let keymgr = new_keymgr();
522
        setup_identity_keys(&keymgr);
523
        keymgr
524
    }
525

            
526
    /// Return a [`Timestamp`] given a [`SystemTime`] rounded down to its nearest second.
527
    ///
528
    /// In other words, the `tv_nsec` of a [`SystemTime`] is dropped.
529
    fn to_timestamp_in_secs(valid_until: SystemTime) -> Timestamp {
530
        use std::time::UNIX_EPOCH;
531
        let seconds = valid_until.duration_since(UNIX_EPOCH).unwrap().as_secs();
532
        Timestamp::from(UNIX_EPOCH + Duration::from_secs(seconds))
533
    }
534

            
535
    /// Call [`try_rotate_keys_no_lock`] with default consensus parameters.
536
    fn rotate_keys(now: SystemTime, keymgr: &KeyMgr) -> anyhow::Result<SystemTime> {
537
        try_rotate_keys(
538
            now,
539
            keymgr,
540
            KeyRotationParams::from(&tor_netdir::params::NetParameters::default()),
541
        )
542
    }
543

            
544
    /// Return the number of keys matching the specified pattern
545
    fn count_keys(keymgr: &KeyMgr, pat: &dyn KeySpecifierPattern) -> usize {
546
        keymgr
547
            .list_matching(&pat.arti_pattern().unwrap())
548
            .unwrap()
549
            .len()
550
    }
551

            
552
    /// Return the number of link keys in the given KeyMgr.
553
    fn count_link_keys(keymgr: &KeyMgr) -> usize {
554
        count_keys(keymgr, &RelayLinkSigningKeypairSpecifierPattern::new_any())
555
    }
556

            
557
    /// Return the number of signing keys in the given KeyMgr.
558
    fn count_signing_keys(keymgr: &KeyMgr) -> usize {
559
        count_keys(keymgr, &RelaySigningKeypairSpecifierPattern::new_any())
560
    }
561

            
562
    /// Return the number of ntor keys in the given KeyMgr.
563
    fn count_ntor_keys(keymgr: &KeyMgr) -> usize {
564
        count_keys(keymgr, &RelayNtorKeypairSpecifierPattern::new_any())
565
    }
566

            
567
    /// Simulate the bootstrap when no keys exists. We should have one link key and one signing key
568
    /// after the first rotation.
569
    #[test]
570
    fn test_initial_key_generation() {
571
        MockRuntime::test_with_various(|runtime| async move {
572
            let keymgr = setup();
573
            let now = runtime.wallclock();
574

            
575
            let next_expiry = rotate_keys(now, &keymgr).unwrap();
576

            
577
            assert_eq!(count_link_keys(&keymgr), 1, "expected one link key");
578
            assert_eq!(count_signing_keys(&keymgr), 1, "expected one signing key");
579
            assert_eq!(count_ntor_keys(&keymgr), 1, "expected one ntor key");
580

            
581
            // The earliest expiry should be the link key (~2 days out).
582
            let expected = runtime.wallclock() + LINK_CERT_LIFETIME;
583
            assert_eq!(
584
                next_expiry, expected,
585
                "next expiry should be ~{LINK_CERT_LIFETIME:?} from now, got {next_expiry:?}"
586
            );
587
        });
588
    }
589

            
590
    /// Calling rotate_keys a second time with fresh keys should indicate no rotation.
591
    #[test]
592
    fn test_rotation_on_fresh_keys() {
593
        MockRuntime::test_with_various(|runtime| async move {
594
            let keymgr = setup();
595
            let now = runtime.wallclock();
596
            let _expiry = rotate_keys(now, &keymgr).unwrap();
597

            
598
            // Advance by 1 hour (inside 2 days of link key).
599
            runtime.advance_by(Duration::from_secs(60 * 60)).await;
600

            
601
            let _expiry = rotate_keys(now, &keymgr).unwrap();
602

            
603
            assert_eq!(count_link_keys(&keymgr), 1, "expected one link key");
604
            assert_eq!(count_signing_keys(&keymgr), 1, "expected one signing key");
605
            assert_eq!(count_ntor_keys(&keymgr), 1, "expected one ntor key");
606
        });
607
    }
608

            
609
    /// Test rotation before and after rotation expiry buffer for the link key.
610
    #[test]
611
    fn test_rotation_link_key() {
612
        MockRuntime::test_with_various(|runtime| async move {
613
            let keymgr = setup();
614
            // First rotation creates the keys.
615
            rotate_keys(runtime.wallclock(), &keymgr).unwrap();
616

            
617
            // Advance to 1 second _before_ the rotation-buffer threshold. We should not rotate
618
            // with this.
619
            let just_before =
620
                LINK_CERT_LIFETIME - KEY_ROTATION_EXPIRE_BUFFER - Duration::from_secs(1);
621
            runtime.advance_by(just_before).await;
622

            
623
            let first_expiry = rotate_keys(runtime.wallclock(), &keymgr).unwrap();
624

            
625
            assert_eq!(count_link_keys(&keymgr), 1, "expected one link key");
626
            assert_eq!(count_signing_keys(&keymgr), 1, "expected one signing key");
627

            
628
            // Move it just after the expiry buffer and expect a rotation.
629
            runtime.advance_by(Duration::from_secs(1)).await;
630

            
631
            let second_expiry = rotate_keys(runtime.wallclock(), &keymgr).unwrap();
632
            assert_ne!(first_expiry, second_expiry);
633
        });
634
    }
635

            
636
    /// Test rotation before and after rotation expiry buffer for the signing key.
637
    #[test]
638
    fn test_rotation_signing_key() {
639
        MockRuntime::test_with_various(|runtime| async move {
640
            let keymgr = setup();
641
            // First rotation creates the keys.
642
            rotate_keys(runtime.wallclock(), &keymgr).unwrap();
643

            
644
            // Closure to get the relay signing key keystore entry.
645
            let get_key_spec = || {
646
                let entries = keymgr
647
                    .list_matching(
648
                        &RelaySigningKeypairSpecifierPattern::new_any()
649
                            .arti_pattern()
650
                            .unwrap(),
651
                    )
652
                    .unwrap();
653
                let entry = entries.first().unwrap();
654
                let spec: RelaySigningKeypairSpecifier = entry.key_path().try_into().unwrap();
655
                spec
656
            };
657

            
658
            // Advance to 1 second _before_ the rotation-buffer threshold. We should not rotate
659
            // with this.
660
            let just_before =
661
                SIGNING_KEY_CERT_LIFETIME - KEY_ROTATION_EXPIRE_BUFFER - Duration::from_secs(1);
662
            runtime.advance_by(just_before).await;
663

            
664
            let _expiry = rotate_keys(runtime.wallclock(), &keymgr).unwrap();
665

            
666
            let spec = get_key_spec();
667
            assert_eq!(
668
                spec.valid_until,
669
                to_timestamp_in_secs(
670
                    runtime.wallclock() + KEY_ROTATION_EXPIRE_BUFFER + Duration::from_secs(1)
671
                ),
672
                "RelaySigningKeypairSpecifier should not have rotated"
673
            );
674

            
675
            assert_eq!(count_link_keys(&keymgr), 1, "expected one link key");
676
            assert_eq!(count_signing_keys(&keymgr), 1, "expected one signing key");
677

            
678
            // Move it just after the expiry buffer and expect a rotation.
679
            runtime.advance_by(Duration::from_secs(1)).await;
680

            
681
            let _expiry = rotate_keys(runtime.wallclock(), &keymgr).unwrap();
682
            let spec = get_key_spec();
683
            assert_eq!(
684
                spec.valid_until,
685
                to_timestamp_in_secs(runtime.wallclock() + SIGNING_KEY_CERT_LIFETIME),
686
                "RelaySigningKeypairSpecifier should have rotated"
687
            );
688
        });
689
    }
690

            
691
    /// Test rotation before and after rotation expiry buffer for the ntor key.
692
    #[test]
693
    fn test_rotation_ntor_key() {
694
        MockRuntime::test_with_various(|runtime| async move {
695
            let keymgr = setup();
696
            // First rotation creates the keys.
697
            rotate_keys(runtime.wallclock(), &keymgr).unwrap();
698

            
699
            // Advance to 1 second _before_ the rotation-buffer threshold. We should not rotate
700
            // with this.
701
            let default_params =
702
                KeyRotationParams::from(&tor_netdir::params::NetParameters::default());
703
            let just_before =
704
                default_params.ntor_lifetime - KEY_ROTATION_EXPIRE_BUFFER - Duration::from_secs(1);
705
            runtime.advance_by(just_before).await;
706

            
707
            let _expiry = rotate_keys(runtime.wallclock(), &keymgr).unwrap();
708
            assert_eq!(count_ntor_keys(&keymgr), 1, "expected one ntor key");
709

            
710
            // Move it just after the expiry buffer and expect a rotation.
711
            runtime.advance_by(Duration::from_secs(1)).await;
712

            
713
            let _expiry = rotate_keys(runtime.wallclock(), &keymgr).unwrap();
714
            assert_eq!(
715
                count_ntor_keys(&keymgr),
716
                2,
717
                "there should be 2 ntor keys in the grace period"
718
            );
719

            
720
            runtime.advance_by(default_params.ntor_grace_period).await;
721

            
722
            let _expiry = rotate_keys(runtime.wallclock(), &keymgr).unwrap();
723
            assert_eq!(
724
                count_ntor_keys(&keymgr),
725
                1,
726
                "the old ntor key should have been removed after the grace period"
727
            );
728
        });
729
    }
730
}