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

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

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

            
20
use crate::{
21
    keys::{
22
        RelayIdentityKeypairSpecifier, RelayLinkSigningKeypairSpecifier,
23
        RelayLinkSigningKeypairSpecifierPattern, RelayNtorKeypairSpecifier,
24
        RelayNtorKeypairSpecifierPattern, RelaySigningKeyCertSpecifier,
25
        RelaySigningKeyCertSpecifierPattern, RelaySigningKeypairSpecifier,
26
        RelaySigningKeypairSpecifierPattern, RelaySigningPublicKeySpecifier, Timestamp,
27
    },
28
    tasks::crypto::{KeyRotationParams, views::FullKeyView},
29
};
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
// The following expiry durations have been taken from C-tor.
39

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

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

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

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

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

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

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

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

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

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

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

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

            
167
224
    Ok(min_valid_until.map(SystemTime::from))
168
224
}
169

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

            
192
112
    Ok(generated)
193
112
}
194

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

            
227
56
    Ok(generated)
228
56
}
229

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

            
252
56
    let cert_expiry = now + SIGNING_KEY_CERT_LIFETIME;
253

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

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

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

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

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

            
301
56
        Ok(all_expired)
302
56
    };
303

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

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

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

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

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

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

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

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

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

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

            
457
56
    Ok(next_expiry)
458
56
}
459

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

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

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

            
498
    use super::*;
499

            
500
    use std::sync::Arc;
501

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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