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
#![warn(clippy::cognitive_complexity)]
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
#![deny(clippy::print_stderr)]
28
#![deny(clippy::print_stdout)]
29
#![warn(clippy::rc_buffer)]
30
#![deny(clippy::ref_option_ref)]
31
#![warn(clippy::semicolon_if_nothing_returned)]
32
#![warn(clippy::trait_duplication_in_bounds)]
33
#![deny(clippy::unchecked_time_subtraction)]
34
#![deny(clippy::unnecessary_wraps)]
35
#![warn(clippy::unseparated_literal_suffix)]
36
#![deny(clippy::unwrap_used)]
37
#![deny(clippy::mod_module_files)]
38
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
39
#![allow(clippy::uninlined_format_args)]
40
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
41
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
42
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
43
#![allow(clippy::needless_lifetimes)] // See arti#1765
44
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
45
#![allow(clippy::collapsible_if)] // See arti#2342
46
#![deny(clippy::unused_async)]
47
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
48

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

            
53
use std::{
54
    sync::Arc,
55
    time::{Duration, SystemTime},
56
};
57

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

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

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

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

            
115
2
    let signer = SigningKey::<Sha256>::new(keypair.as_key().clone());
116

            
117
2
    let mut builder = CertificateBuilder::new(
118
2
        self_signed_profile,
119
2
        serial_number,
120
2
        validity,
121
2
        subject,
122
2
        spki,
123
2
        &signer,
124
    )?;
125

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

            
133
2
    let cert = builder.build()?;
134

            
135
2
    let mut output = Vec::new();
136
2
    let _ignore_length: x509_cert::der::Length = cert
137
2
        .encode_to_vec(&mut output)
138
2
        .map_err(X509CertError::CouldNotEncode)?;
139
2
    Ok(output)
140
2
}
141

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

            
158
    /// A private key for use in the TLS handshake.
159
    private_key: ecdsa::SigningKey<p256::NistP256>,
160

            
161
    /// A SHA256 digest of the link certificate
162
    /// (the one certifying the private key's public component).
163
    ///
164
    /// This digest is the one what will be certified by the relay's
165
    /// `SIGNING_V_TLS_CERT`
166
    /// certificate.
167
    sha256_digest: [u8; 32],
168

            
169
    /// A time after which this set of link information won't be valid,
170
    /// and another should be generated.
171
    expiration: SystemTime,
172
}
173

            
174
/// What lifetime do we pick for a TLS certificate, in days?
175
const TLS_CERT_LIFETIME_DAYS: u32 = 30;
176

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

            
209
    /// Return the SHA256 digest of the link certificate
210
    ///
211
    /// This digest is the one certified with the relay's
212
    /// `SIGNING_V_TLS_CERT`
213
    /// certificate.
214
    pub fn link_cert_sha256(&self) -> &[u8; 32] {
215
        &self.sha256_digest
216
    }
217

            
218
    /// Create a new TLS link key and associated certificate(s).
219
    ///
220
    /// The certificate will be valid at `now`, and for a while after.
221
    ///
222
    /// The certificate parameters and keys are chosen for reasonable security,
223
    /// approximate conformance to RFC5280, and limited fingerprinting resistance.
224
    ///
225
    /// Note: The fingerprinting resistance is quite limited.
226
    /// We will likely want to pursue these avenues for better fingerprinting resistance:
227
    ///
228
    /// - Encourage more use of TLS 1.3, where server certificates are encrypted.
229
    ///   (This prevents passive fingerprinting only.)
230
    /// - Adjust this function to make certificates look even more normal
231
    /// - Integrate with ACME-supporting certificate issuers (Letsencrypt, etc)
232
    ///   to get real certificates for Tor relays.
233
16
    pub fn create<Rng: CryptoRng>(
234
16
        rng: &mut Rng,
235
16
        now: SystemTime,
236
16
        issuer_hostname: &str,
237
16
        subject_hostname: &str,
238
16
    ) -> Result<Self, X509CertError> {
239
        // We choose to use p256 here as the most commonly used elliptic curve
240
        // group for X.509 web certificate signing, as of this writing.
241
        //
242
        // We want to use an elliptic curve here for its higher security/performance ratio than RSA,
243
        // and for its _much_ faster key generation time.
244
16
        let private_key = p256::ecdsa::SigningKey::random(&mut RngCompat::new(&mut *rng));
245
16
        let public_key = p256::ecdsa::VerifyingKey::from(&private_key);
246

            
247
        // Note that we'll discard this key after signing the certificate with it:
248
        // The real certification for private_key is done in the SIGNING_V_TLS_CERT
249
        // certificate.
250
16
        let issuer_private_key = p256::ecdsa::SigningKey::random(&mut RngCompat::new(&mut *rng));
251

            
252
        // NOTE: This is how C Tor builds its DNs, but that doesn't mean it's a good idea.
253
16
        let issuer = format!("CN={issuer_hostname}")
254
16
            .parse()
255
16
            .map_err(X509CertError::InvalidHostname)?;
256
16
        let subject: x509_cert::name::Name = format!("CN={subject_hostname}")
257
16
            .parse()
258
16
            .map_err(X509CertError::InvalidHostname)?;
259

            
260
16
        let self_signed_profile = Profile::Leaf {
261
16
            issuer,
262
16
            enable_key_agreement: true,
263
16
            enable_key_encipherment: true,
264
16
            include_subject_key_identifier: true,
265
16
        };
266
16
        let serial_number = random_serial_number(rng)?;
267
16
        let (validity, expiration) = cert_validity(now, TLS_CERT_LIFETIME_DAYS)?;
268
16
        let spki = SubjectPublicKeyInfo::from_key(public_key)?;
269

            
270
16
        let builder = CertificateBuilder::new(
271
16
            self_signed_profile,
272
16
            serial_number,
273
16
            validity,
274
16
            subject,
275
16
            spki,
276
16
            &issuer_private_key,
277
        )?;
278

            
279
16
        let cert = builder.build::<ecdsa::der::Signature<_>>()?;
280

            
281
16
        let mut certificate_der = Vec::new();
282
16
        let _ignore_length: x509_cert::der::Length = cert
283
16
            .encode_to_vec(&mut certificate_der)
284
16
            .map_err(X509CertError::CouldNotEncode)?;
285

            
286
16
        let sha256_digest = tor_llcrypto::d::Sha256::digest(&certificate_der).into();
287
16
        let certificates = vec![certificate_der];
288

            
289
16
        Ok(TlsKeyAndCert {
290
16
            certificates,
291
16
            private_key,
292
16
            sha256_digest,
293
16
            expiration,
294
16
        })
295
16
    }
296
}
297

            
298
/// Return a Validity that includes `now`, and lasts for `lifetime_days` additionally.
299
///
300
/// Additionally, return the time at which the certificate expires.
301
///
302
/// We ensure that our cert is valid at least a day into the past.
303
///
304
/// We obfuscate our current time a little by rounding to the nearest midnight UTC.
305
543
fn cert_validity(
306
543
    now: SystemTime,
307
543
    lifetime_days: u32,
308
543
) -> Result<(Validity, SystemTime), X509CertError> {
309
    const ONE_DAY: Duration = Duration::new(86400, 0);
310

            
311
1095
    let start_of_day_containing = |when| -> Result<_, X509CertError> {
312
1086
        let dt = DateTime::from_system_time(when)
313
1086
            .map_err(into_internal!("Couldn't represent time as a DER DateTime"))?;
314
1086
        let dt = DateTime::new(dt.year(), dt.month(), dt.day(), 0, 0, 0)
315
1086
            .map_err(into_internal!("Couldn't construct DER DateTime"))?;
316
1086
        Ok(x509_cert::time::Time::GeneralTime(
317
1086
            GeneralizedTime::from_date_time(dt),
318
1086
        ))
319
1086
    };
320

            
321
543
    let start_on_day = now - ONE_DAY;
322
543
    let end_on_day = start_on_day + ONE_DAY * lifetime_days;
323

            
324
543
    let validity = Validity {
325
543
        not_before: start_of_day_containing(start_on_day)?,
326
543
        not_after: start_of_day_containing(end_on_day)?,
327
    };
328
543
    let expiration = validity.not_after.into();
329
543
    Ok((validity, expiration))
330
543
}
331

            
332
/// Return a random serial number for use in a new certificate.
333
18
fn random_serial_number<Rng: CryptoRng>(rng: &mut Rng) -> Result<SerialNumber, X509CertError> {
334
    const SER_NUMBER_LEN: usize = 16;
335
18
    let mut buf = [0; SER_NUMBER_LEN];
336
18
    rng.fill_bytes(&mut buf[..]);
337
18
    Ok(SerialNumber::new(&buf[..]).map_err(into_internal!("Couldn't construct serial number!"))?)
338
18
}
339

            
340
/// An error that has occurred while trying to create a certificate.
341
#[derive(Clone, Debug, thiserror::Error)]
342
#[non_exhaustive]
343
pub enum X509CertError {
344
    /// We received a signing key that we can't use.
345
    #[error("Provided signing key not valid: {0}")]
346
    InvalidSigningKey(String),
347

            
348
    /// We received a subject key that we can't use.
349
    #[error("Couldn't use provided key as a subject")]
350
    SubjectKeyError(#[from] x509_cert::spki::Error),
351

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

            
357
    /// We couldn't construct the certificate.
358
    #[error("Unable to build certificate")]
359
    CouldNotBuild(#[source] Arc<x509_cert::builder::Error>),
360

            
361
    /// We constructed the certificate, but couldn't encode it as DER.
362
    #[error("Unable to encode certificate")]
363
    CouldNotEncode(#[source] x509_cert::der::Error),
364

            
365
    /// We constructed a key but couldn't format it as PKCS8.
366
    #[error("Unable to format key as PKCS8")]
367
    CouldNotFormatPkcs8(#[source] p256::pkcs8::Error),
368

            
369
    /// We've encountered some kind of a bug.
370
    #[error("Internal error while creating certificate")]
371
    Bug(#[from] tor_error::Bug),
372
}
373

            
374
impl From<x509_cert::builder::Error> for X509CertError {
375
    fn from(value: x509_cert::builder::Error) -> Self {
376
        X509CertError::CouldNotBuild(Arc::new(value))
377
    }
378
}
379

            
380
#[cfg(test)]
381
mod test {
382
    // @@ begin test lint list maintained by maint/add_warning @@
383
    #![allow(clippy::bool_assert_comparison)]
384
    #![allow(clippy::clone_on_copy)]
385
    #![allow(clippy::dbg_macro)]
386
    #![allow(clippy::mixed_attributes_style)]
387
    #![allow(clippy::print_stderr)]
388
    #![allow(clippy::print_stdout)]
389
    #![allow(clippy::single_char_pattern)]
390
    #![allow(clippy::unwrap_used)]
391
    #![allow(clippy::unchecked_time_subtraction)]
392
    #![allow(clippy::useless_vec)]
393
    #![allow(clippy::needless_pass_by_value)]
394
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
395

            
396
    use super::*;
397
    use tor_basic_utils::test_rng::testing_rng;
398

            
399
    #[test]
400
    fn identity_cert_generation() {
401
        let mut rng = testing_rng();
402
        let keypair = RsaKeypair::generate(&mut rng).unwrap();
403
        let cert = create_legacy_rsa_id_cert(
404
            &mut rng,
405
            SystemTime::now(),
406
            "www.house-of-pancakes.example.com",
407
            &keypair,
408
        )
409
        .unwrap();
410

            
411
        let key_extracted = tor_llcrypto::util::x509_extract_rsa_subject_kludge(&cert[..]).unwrap();
412
        assert_eq!(key_extracted, keypair.to_public_key());
413

            
414
        // TODO: It would be neat to validate this certificate with an independent x509 implementation,
415
        // but afaict most of them sensibly refuse to handle RSA1024.
416
        //
417
        // I've checked the above-generated cert using `openssl verify`, but that's it.
418
    }
419

            
420
    #[test]
421
    fn tls_cert_info() {
422
        let mut rng = testing_rng();
423
        let certified = TlsKeyAndCert::create(
424
            &mut rng,
425
            SystemTime::now(),
426
            "foo.example.com",
427
            "bar.example.com",
428
        )
429
        .unwrap();
430
        dbg!(certified);
431
    }
432
}