1
#![cfg_attr(docsrs, feature(doc_cfg))]
2
#![doc = include_str!("../README.md")]
3
// @@ begin lint list maintained by maint/add_warning @@
4
#![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
5
#![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
6
#![warn(missing_docs)]
7
#![warn(noop_method_call)]
8
#![warn(unreachable_pub)]
9
#![warn(clippy::all)]
10
#![deny(clippy::await_holding_lock)]
11
#![deny(clippy::cargo_common_metadata)]
12
#![deny(clippy::cast_lossless)]
13
#![deny(clippy::checked_conversions)]
14
#![allow(clippy::cognitive_complexity)] // See arti#2556
15
#![deny(clippy::debug_assert_with_mut_call)]
16
#![deny(clippy::exhaustive_enums)]
17
#![deny(clippy::exhaustive_structs)]
18
#![deny(clippy::expl_impl_clone_on_copy)]
19
#![deny(clippy::fallible_impl_from)]
20
#![deny(clippy::implicit_clone)]
21
#![deny(clippy::large_stack_arrays)]
22
#![warn(clippy::manual_ok_or)]
23
#![deny(clippy::missing_docs_in_private_items)]
24
#![warn(clippy::needless_borrow)]
25
#![warn(clippy::needless_pass_by_value)]
26
#![warn(clippy::option_option)]
27
#![warn(clippy::or_fun_call)]
28
#![deny(clippy::print_stderr)]
29
#![deny(clippy::print_stdout)]
30
#![warn(clippy::rc_buffer)]
31
#![deny(clippy::ref_option_ref)]
32
#![warn(clippy::semicolon_if_nothing_returned)]
33
#![warn(clippy::trait_duplication_in_bounds)]
34
#![deny(clippy::unchecked_time_subtraction)]
35
#![deny(clippy::unnecessary_wraps)]
36
#![warn(clippy::unseparated_literal_suffix)]
37
#![deny(clippy::unwrap_used)]
38
#![deny(clippy::mod_module_files)]
39
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
40
#![allow(clippy::uninlined_format_args)]
41
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
42
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
43
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
44
#![allow(clippy::needless_lifetimes)] // See arti#1765
45
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
46
#![allow(clippy::collapsible_if)] // See arti#2342
47
#![deny(clippy::unused_async)]
48
#![deny(clippy::string_slice)] // See arti#2571
49
#![allow(recursion_depth_exceeding_limit)] // arti#2715, rust/issues/159228
50
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
51

            
52
// This module uses the `x509-cert` crate to generate certificates;
53
// if we decide to switch, `rcgen` and `x509-certificate`
54
// seem like the likeliest options.
55

            
56
use std::{
57
    sync::Arc,
58
    time::{Duration, SystemTime},
59
};
60

            
61
use digest::Digest;
62
use rand::CryptoRng;
63
use rsa::pkcs8::{EncodePrivateKey as _, SubjectPublicKeyInfo};
64
use tor_error::into_internal;
65
use tor_llcrypto::{pk::rsa::KeyPair as RsaKeypair, util::rng::RngCompat};
66
use x509_cert::{
67
    builder::{Builder, CertificateBuilder, Profile},
68
    der::{DateTime, Encode, asn1::GeneralizedTime, zeroize::Zeroizing},
69
    ext::pkix::{KeyUsage, KeyUsages},
70
    serial_number::SerialNumber,
71
    time::Validity,
72
};
73

            
74
/// Legacy identity keys are required to have this length.
75
const EXPECT_ID_BITS: usize = 1024;
76
/// Legacy identity keys are required to have this exponent.
77
const EXPECT_ID_EXPONENT: u32 = 65537;
78
/// Lifetime of generated id certs, in days.
79
const ID_CERT_LIFETIME_DAYS: u32 = 365;
80

            
81
/// Create an X.509 certificate, for use in a CERTS cell,
82
/// self-certifying the provided RSA identity key.
83
///
84
/// The resulting certificate will be encoded in DER.
85
/// Its cert_type field should be 02 when it is sent in a CERTS cell.
86
///
87
/// The resulting certificate is quite minimal, and has no unnecessary extensions.
88
///
89
/// Returns an error on failure, or if `keypair` is not a 1024-bit RSA key
90
/// with exponent of 65537.
91
5
pub fn create_legacy_rsa_id_cert<Rng: CryptoRng>(
92
5
    rng: &mut Rng,
93
5
    now: SystemTime,
94
5
    hostname: &str,
95
5
    keypair: &RsaKeypair,
96
5
) -> Result<Vec<u8>, X509CertError> {
97
    use rsa::pkcs1v15::SigningKey;
98
    use tor_llcrypto::d::Sha256;
99
5
    let public = keypair.to_public_key();
100
5
    if !public.exponent_is(EXPECT_ID_EXPONENT) {
101
        return Err(X509CertError::InvalidSigningKey("Invalid exponent".into()));
102
5
    }
103
5
    if public.bits() != EXPECT_ID_BITS {
104
1
        return Err(X509CertError::InvalidSigningKey(
105
1
            "Invalid key length".into(),
106
1
        ));
107
4
    }
108

            
109
4
    let self_signed_profile = Profile::Manual { issuer: None };
110
4
    let serial_number = random_serial_number(rng)?;
111
4
    let (validity, _) = cert_validity(now, ID_CERT_LIFETIME_DAYS)?;
112
    // NOTE: This is how C Tor builds its DNs, but that doesn't mean it's a good idea.
113
4
    let subject: x509_cert::name::Name = format!("CN={hostname}")
114
4
        .parse()
115
4
        .map_err(X509CertError::InvalidHostname)?;
116
4
    let spki = SubjectPublicKeyInfo::from_key(keypair.to_public_key().as_key().clone())?;
117

            
118
4
    let signer = SigningKey::<Sha256>::new(keypair.as_key().clone());
119

            
120
4
    let mut builder = CertificateBuilder::new(
121
4
        self_signed_profile,
122
4
        serial_number,
123
4
        validity,
124
4
        subject,
125
4
        spki,
126
4
        &signer,
127
    )?;
128

            
129
    // We do not, strictly speaking, need this extension: Tor doesn't care that it's there.
130
    // We do, however, need _some_ extension, or else we'll generate a v1 certificate,
131
    // which we don't want to do.
132
4
    builder.add_extension(&KeyUsage(
133
4
        KeyUsages::KeyCertSign | KeyUsages::DigitalSignature,
134
4
    ))?;
135

            
136
4
    let cert = builder.build()?;
137

            
138
4
    let mut output = Vec::new();
139
4
    let _ignore_length: x509_cert::der::Length = cert
140
4
        .encode_to_vec(&mut output)
141
4
        .map_err(X509CertError::CouldNotEncode)?;
142
4
    Ok(output)
143
5
}
144

            
145
/// A set of x.509 certificate information and keys for use with a TLS library.
146
///
147
/// Only relays need this: They should set these as the certificate(s) to be used
148
/// for incoming TLS connections.
149
///
150
/// This is not necessarily the most convenient form to manipulate certificates in:
151
/// rather, it is intended to provide the formats that TLS libraries generally
152
/// expect to get.
153
#[derive(Clone, Debug)]
154
#[non_exhaustive]
155
pub struct TlsKeyAndCert {
156
    /// A list of certificates in DER form.
157
    ///
158
    /// (This may contain more than one certificate, but for now only one certificate is used.)
159
    certificates: Vec<Vec<u8>>,
160

            
161
    /// A private key for use in the TLS handshake.
162
    //
163
    // Disabled:
164
    // private_key: ecdsa::SigningKey<p256::NistP256>,
165
    private_key: rsa::RsaPrivateKey,
166

            
167
    /// A SHA256 digest of the link certificate
168
    /// (the one certifying the private key's public component).
169
    ///
170
    /// This digest is the one what will be certified by the relay's
171
    /// `SIGNING_V_TLS_CERT`
172
    /// certificate.
173
    sha256_digest: [u8; 32],
174

            
175
    /// A time after which this set of link information won't be valid,
176
    /// and another should be generated.
177
    expiration: SystemTime,
178
}
179

            
180
/// What lifetime do we pick for a TLS certificate, in days?
181
const TLS_CERT_LIFETIME_DAYS: u32 = 30;
182

            
183
impl TlsKeyAndCert {
184
    /// Return the certificates as a list of DER-encoded values.
185
1027
    pub fn certificates_der(&self) -> Vec<&[u8]> {
186
1027
        self.certificates.iter().map(|der| der.as_ref()).collect()
187
1027
    }
188
    /// Return the certificates as a concatenated list in PEM ("BEGIN CERTIFICATE") format.
189
    pub fn certificate_pem(&self) -> String {
190
        let config = pem::EncodeConfig::new().set_line_ending(pem::LineEnding::LF);
191
        self.certificates
192
            .iter()
193
            .map(|der| pem::encode_config(&pem::Pem::new("CERTIFICATE", &der[..]), config))
194
            .collect()
195
    }
196
    /// Return the private key in (unencrypted) PKCS8 DER format.
197
237
    pub fn private_key_pkcs8_der(&self) -> Result<Zeroizing<Vec<u8>>, X509CertError> {
198
237
        Ok(self
199
237
            .private_key
200
237
            .to_pkcs8_der()
201
237
            .map_err(X509CertError::CouldNotFormatPkcs8)?
202
237
            .to_bytes())
203
237
    }
204
    /// Return the private key in (unencrypted) PKCS8 PEM ("BEGIN PRIVATE KEY") format.
205
    pub fn private_key_pkcs8_pem(&self) -> Result<Zeroizing<String>, X509CertError> {
206
        self.private_key
207
            .to_pkcs8_pem(p256::pkcs8::LineEnding::LF)
208
            .map_err(X509CertError::CouldNotFormatPkcs8)
209
    }
210
    /// Return the earliest time at which any of these certificates will expire.
211
    pub fn expiration(&self) -> SystemTime {
212
        self.expiration
213
    }
214

            
215
    /// Return the SHA256 digest of the link certificate
216
    ///
217
    /// This digest is the one certified with the relay's
218
    /// `SIGNING_V_TLS_CERT`
219
    /// certificate.
220
237
    pub fn link_cert_sha256(&self) -> &[u8; 32] {
221
237
        &self.sha256_digest
222
237
    }
223

            
224
    /// Create a new TLS link key and associated certificate(s).
225
    ///
226
    /// The certificate will be valid at `now`, and for a while after.
227
    ///
228
    /// The certificate parameters and keys are chosen for reasonable security,
229
    /// approximate conformance to RFC5280, and limited fingerprinting resistance.
230
    ///
231
    /// Note: The fingerprinting resistance is quite limited.
232
    /// We will likely want to pursue these avenues for better fingerprinting resistance:
233
    ///
234
    /// - Encourage more use of TLS 1.3, where server certificates are encrypted.
235
    ///   (This prevents passive fingerprinting only.)
236
    /// - Adjust this function to make certificates look even more normal
237
    /// - Integrate with ACME-supporting certificate issuers (Letsencrypt, etc)
238
    ///   to get real certificates for Tor relays.
239
11
    pub fn create<Rng: CryptoRng>(
240
11
        rng: &mut Rng,
241
11
        now: SystemTime,
242
11
        issuer_hostname: &str,
243
11
        subject_hostname: &str,
244
11
    ) -> Result<Self, X509CertError> {
245
        // We would prefer to use p256 here, since it is the most commonly used elliptic curve
246
        // group for X.509 web certificate signing, as of this writing.
247
        //
248
        // We want to use an elliptic curve here for its higher security/performance ratio than RSA,
249
        // and for its _much_ faster key generation time.
250
        //
251
        // But unfortunately, we can't: C tor has a bug where if the subject key is not RSA,
252
        // the connection will be closed with an error:
253
        // <https://gitlab.torproject.org/tpo/core/tor/-/issues/41226>.
254
        // If this bug is fixed, then we will have to wait until all clients and servers upgrade
255
        // before we can send p256 subject keys instead.
256
        //
257
        // DISABLED:
258
        // let private_key = rsa::RsaPrivateKey::p256::ecdsa::SigningKey::random(&mut RngCompat::new(&mut *rng));
259
        // let public_key = p256::ecdsa::VerifyingKey::from(&private_key);
260

            
261
        const RSA_KEY_BITS: usize = 2048;
262
11
        let private_key = rsa::RsaPrivateKey::new(&mut RngCompat::new(&mut *rng), RSA_KEY_BITS)
263
11
            .map_err(into_internal!("Unable to generate RSA key"))?;
264
11
        let public_key = private_key.to_public_key();
265

            
266
        // Note that we'll discard this key after signing the certificate with it:
267
        // The real certification for private_key is done in the SIGNING_V_TLS_CERT
268
        // certificate.
269
11
        let issuer_private_key = p256::ecdsa::SigningKey::random(&mut RngCompat::new(&mut *rng));
270

            
271
        // NOTE: This is how C Tor builds its DNs, but that doesn't mean it's a good idea.
272
11
        let issuer = format!("CN={issuer_hostname}")
273
11
            .parse()
274
11
            .map_err(X509CertError::InvalidHostname)?;
275
11
        let subject: x509_cert::name::Name = format!("CN={subject_hostname}")
276
11
            .parse()
277
11
            .map_err(X509CertError::InvalidHostname)?;
278

            
279
11
        let self_signed_profile = Profile::Leaf {
280
11
            issuer,
281
11
            enable_key_agreement: true,
282
11
            enable_key_encipherment: true,
283
11
            include_subject_key_identifier: true,
284
11
        };
285
11
        let serial_number = random_serial_number(rng)?;
286
11
        let (validity, expiration) = cert_validity(now, TLS_CERT_LIFETIME_DAYS)?;
287
11
        let spki = SubjectPublicKeyInfo::from_key(public_key)?;
288

            
289
11
        let builder = CertificateBuilder::new(
290
11
            self_signed_profile,
291
11
            serial_number,
292
11
            validity,
293
11
            subject,
294
11
            spki,
295
11
            &issuer_private_key,
296
        )?;
297

            
298
11
        let cert = builder.build::<ecdsa::der::Signature<_>>()?;
299

            
300
11
        let mut certificate_der = Vec::new();
301
11
        let _ignore_length: x509_cert::der::Length = cert
302
11
            .encode_to_vec(&mut certificate_der)
303
11
            .map_err(X509CertError::CouldNotEncode)?;
304

            
305
11
        let sha256_digest = tor_llcrypto::d::Sha256::digest(&certificate_der).into();
306
11
        let certificates = vec![certificate_der];
307

            
308
11
        Ok(TlsKeyAndCert {
309
11
            certificates,
310
11
            private_key,
311
11
            sha256_digest,
312
11
            expiration,
313
11
        })
314
11
    }
315
}
316

            
317
/// Return a Validity that includes `now`, and lasts for `lifetime_days` additionally.
318
///
319
/// Additionally, return the time at which the certificate expires.
320
///
321
/// We ensure that our cert is valid at least a day into the past.
322
///
323
/// We obfuscate our current time a little by rounding to the nearest midnight UTC.
324
1029
fn cert_validity(
325
1029
    now: SystemTime,
326
1029
    lifetime_days: u32,
327
1029
) -> Result<(Validity, SystemTime), X509CertError> {
328
    const ONE_DAY: Duration = Duration::new(86400, 0);
329

            
330
2058
    let start_of_day_containing = |when| -> Result<_, X509CertError> {
331
2058
        let dt = DateTime::from_system_time(when)
332
2058
            .map_err(into_internal!("Couldn't represent time as a DER DateTime"))?;
333
2058
        let dt = DateTime::new(dt.year(), dt.month(), dt.day(), 0, 0, 0)
334
2058
            .map_err(into_internal!("Couldn't construct DER DateTime"))?;
335
2058
        Ok(x509_cert::time::Time::GeneralTime(
336
2058
            GeneralizedTime::from_date_time(dt),
337
2058
        ))
338
2058
    };
339

            
340
1029
    let start_on_day = now - ONE_DAY;
341
1029
    let end_on_day = start_on_day + ONE_DAY * lifetime_days;
342

            
343
1029
    let validity = Validity {
344
1029
        not_before: start_of_day_containing(start_on_day)?,
345
1029
        not_after: start_of_day_containing(end_on_day)?,
346
    };
347
1029
    let expiration = validity.not_after.into();
348
1029
    Ok((validity, expiration))
349
1029
}
350

            
351
/// Return a random serial number for use in a new certificate.
352
15
fn random_serial_number<Rng: CryptoRng>(rng: &mut Rng) -> Result<SerialNumber, X509CertError> {
353
    const SER_NUMBER_LEN: usize = 16;
354
15
    let mut buf = [0; SER_NUMBER_LEN];
355
15
    rng.fill_bytes(&mut buf[..]);
356
15
    Ok(SerialNumber::new(&buf[..]).map_err(into_internal!("Couldn't construct serial number!"))?)
357
15
}
358

            
359
/// An error that has occurred while trying to create a certificate.
360
#[derive(Clone, Debug, thiserror::Error)]
361
#[non_exhaustive]
362
pub enum X509CertError {
363
    /// We received a signing key that we can't use.
364
    #[error("Provided signing key not valid: {0}")]
365
    InvalidSigningKey(String),
366

            
367
    /// We received a subject key that we can't use.
368
    #[error("Couldn't use provided key as a subject")]
369
    SubjectKeyError(#[from] x509_cert::spki::Error),
370

            
371
    /// We received a hostname that we couldn't use:
372
    /// probably, it contained an equals sign or a comma.
373
    #[error("Unable to set hostname when creating certificate")]
374
    InvalidHostname(#[source] x509_cert::der::Error),
375

            
376
    /// We couldn't construct the certificate.
377
    #[error("Unable to build certificate")]
378
    CouldNotBuild(#[source] Arc<x509_cert::builder::Error>),
379

            
380
    /// We constructed the certificate, but couldn't encode it as DER.
381
    #[error("Unable to encode certificate")]
382
    CouldNotEncode(#[source] x509_cert::der::Error),
383

            
384
    /// We constructed a key but couldn't format it as PKCS8.
385
    #[error("Unable to format key as PKCS8")]
386
    CouldNotFormatPkcs8(#[source] p256::pkcs8::Error),
387

            
388
    /// We've encountered some kind of a bug.
389
    #[error("Internal error while creating certificate")]
390
    Bug(#[from] tor_error::Bug),
391
}
392

            
393
impl From<x509_cert::builder::Error> for X509CertError {
394
    fn from(value: x509_cert::builder::Error) -> Self {
395
        X509CertError::CouldNotBuild(Arc::new(value))
396
    }
397
}
398

            
399
#[cfg(test)]
400
mod test {
401
    // @@ begin test lint list maintained by maint/add_warning @@
402
    #![allow(clippy::bool_assert_comparison)]
403
    #![allow(clippy::clone_on_copy)]
404
    #![allow(clippy::dbg_macro)]
405
    #![allow(clippy::mixed_attributes_style)]
406
    #![allow(clippy::print_stderr)]
407
    #![allow(clippy::print_stdout)]
408
    #![allow(clippy::single_char_pattern)]
409
    #![allow(clippy::unwrap_used)]
410
    #![allow(clippy::unchecked_time_subtraction)]
411
    #![allow(clippy::useless_vec)]
412
    #![allow(clippy::needless_pass_by_value)]
413
    #![allow(clippy::string_slice)] // See arti#2571
414
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
415

            
416
    use super::*;
417
    use tor_basic_utils::test_rng::testing_rng;
418
    use web_time_compat::SystemTimeExt;
419

            
420
    #[test]
421
    fn identity_cert_generation() {
422
        let mut rng = testing_rng();
423
        let keypair = RsaKeypair::generate(&mut rng).unwrap();
424
        let cert = create_legacy_rsa_id_cert(
425
            &mut rng,
426
            SystemTime::get(),
427
            "www.house-of-pancakes.example.com",
428
            &keypair,
429
        )
430
        .unwrap();
431

            
432
        let key_extracted = tor_llcrypto::util::x509_extract_rsa_subject_kludge(&cert[..]).unwrap();
433
        assert_eq!(key_extracted, keypair.to_public_key());
434

            
435
        // TODO: It would be neat to validate this certificate with an independent x509 implementation,
436
        // but afaict most of them sensibly refuse to handle RSA1024.
437
        //
438
        // I've checked the above-generated cert using `openssl verify`, but that's it.
439
    }
440

            
441
    #[test]
442
    fn identity_cert_generation_size_check() {
443
        let mut rng = testing_rng();
444
        let keypair = rsa::RsaPrivateKey::new(&mut RngCompat::new(&mut rng), 2048).unwrap();
445
        let keypair: RsaKeypair = keypair.into();
446

            
447
        // This fails since it's not 1024 bits.
448
        assert!(
449
            create_legacy_rsa_id_cert(
450
                &mut rng,
451
                SystemTime::get(),
452
                "www.house-of-pancakes.example.com",
453
                &keypair,
454
            )
455
            .unwrap_err()
456
            .to_string()
457
            .contains("Invalid key length")
458
        );
459
    }
460

            
461
    #[test]
462
    fn tls_cert_info() {
463
        let mut rng = testing_rng();
464
        let certified = TlsKeyAndCert::create(
465
            &mut rng,
466
            SystemTime::get(),
467
            "foo.example.com",
468
            "bar.example.com",
469
        )
470
        .unwrap();
471
        dbg!(certified);
472
    }
473
}