1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![doc = include_str!("../README.md")]
3#![allow(renamed_and_removed_lints)] #![allow(unknown_lints)] #![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)] #![allow(clippy::uninlined_format_args)]
40#![allow(clippy::significant_drop_in_scrutinee)] #![allow(clippy::result_large_err)] #![allow(clippy::needless_raw_string_hashes)] #![allow(clippy::needless_lifetimes)] #![allow(mismatched_lifetime_syntaxes)] #![allow(clippy::collapsible_if)] #![deny(clippy::unused_async)]
47#![deny(clippy::string_slice)] use std::{
55 sync::Arc,
56 time::{Duration, SystemTime},
57};
58
59use digest::Digest;
60use rand::CryptoRng;
61use rsa::pkcs8::{EncodePrivateKey as _, SubjectPublicKeyInfo};
62use tor_error::into_internal;
63use tor_llcrypto::{pk::rsa::KeyPair as RsaKeypair, util::rng::RngCompat};
64use x509_cert::{
65 builder::{Builder, CertificateBuilder, Profile},
66 der::{DateTime, Encode, asn1::GeneralizedTime, zeroize::Zeroizing},
67 ext::pkix::{KeyUsage, KeyUsages},
68 serial_number::SerialNumber,
69 time::Validity,
70};
71
72const EXPECT_ID_BITS: usize = 1024;
74const EXPECT_ID_EXPONENT: u32 = 65537;
76const ID_CERT_LIFETIME_DAYS: u32 = 365;
78
79pub fn create_legacy_rsa_id_cert<Rng: CryptoRng>(
90 rng: &mut Rng,
91 now: SystemTime,
92 hostname: &str,
93 keypair: &RsaKeypair,
94) -> Result<Vec<u8>, X509CertError> {
95 use rsa::pkcs1v15::SigningKey;
96 use tor_llcrypto::d::Sha256;
97 let public = keypair.to_public_key();
98 if !public.exponent_is(EXPECT_ID_EXPONENT) {
99 return Err(X509CertError::InvalidSigningKey("Invalid exponent".into()));
100 }
101 if !public.bits() == EXPECT_ID_BITS {
102 return Err(X509CertError::InvalidSigningKey(
103 "Invalid key length".into(),
104 ));
105 }
106
107 let self_signed_profile = Profile::Manual { issuer: None };
108 let serial_number = random_serial_number(rng)?;
109 let (validity, _) = cert_validity(now, ID_CERT_LIFETIME_DAYS)?;
110 let subject: x509_cert::name::Name = format!("CN={hostname}")
112 .parse()
113 .map_err(X509CertError::InvalidHostname)?;
114 let spki = SubjectPublicKeyInfo::from_key(keypair.to_public_key().as_key().clone())?;
115
116 let signer = SigningKey::<Sha256>::new(keypair.as_key().clone());
117
118 let mut builder = CertificateBuilder::new(
119 self_signed_profile,
120 serial_number,
121 validity,
122 subject,
123 spki,
124 &signer,
125 )?;
126
127 builder.add_extension(&KeyUsage(
131 KeyUsages::KeyCertSign | KeyUsages::DigitalSignature,
132 ))?;
133
134 let cert = builder.build()?;
135
136 let mut output = Vec::new();
137 let _ignore_length: x509_cert::der::Length = cert
138 .encode_to_vec(&mut output)
139 .map_err(X509CertError::CouldNotEncode)?;
140 Ok(output)
141}
142
143#[derive(Clone, Debug)]
152#[non_exhaustive]
153pub struct TlsKeyAndCert {
154 certificates: Vec<Vec<u8>>,
158
159 private_key: rsa::RsaPrivateKey,
164
165 sha256_digest: [u8; 32],
172
173 expiration: SystemTime,
176}
177
178const TLS_CERT_LIFETIME_DAYS: u32 = 30;
180
181impl TlsKeyAndCert {
182 pub fn certificates_der(&self) -> Vec<&[u8]> {
184 self.certificates.iter().map(|der| der.as_ref()).collect()
185 }
186 pub fn certificate_pem(&self) -> String {
188 let config = pem::EncodeConfig::new().set_line_ending(pem::LineEnding::LF);
189 self.certificates
190 .iter()
191 .map(|der| pem::encode_config(&pem::Pem::new("CERTIFICATE", &der[..]), config))
192 .collect()
193 }
194 pub fn private_key_pkcs8_der(&self) -> Result<Zeroizing<Vec<u8>>, X509CertError> {
196 Ok(self
197 .private_key
198 .to_pkcs8_der()
199 .map_err(X509CertError::CouldNotFormatPkcs8)?
200 .to_bytes())
201 }
202 pub fn private_key_pkcs8_pem(&self) -> Result<Zeroizing<String>, X509CertError> {
204 self.private_key
205 .to_pkcs8_pem(p256::pkcs8::LineEnding::LF)
206 .map_err(X509CertError::CouldNotFormatPkcs8)
207 }
208 pub fn expiration(&self) -> SystemTime {
210 self.expiration
211 }
212
213 pub fn link_cert_sha256(&self) -> &[u8; 32] {
219 &self.sha256_digest
220 }
221
222 pub fn create<Rng: CryptoRng>(
238 rng: &mut Rng,
239 now: SystemTime,
240 issuer_hostname: &str,
241 subject_hostname: &str,
242 ) -> Result<Self, X509CertError> {
243 const RSA_KEY_BITS: usize = 2048;
260 let private_key = rsa::RsaPrivateKey::new(&mut RngCompat::new(&mut *rng), RSA_KEY_BITS)
261 .map_err(into_internal!("Unable to generate RSA key"))?;
262 let public_key = private_key.to_public_key();
263
264 let issuer_private_key = p256::ecdsa::SigningKey::random(&mut RngCompat::new(&mut *rng));
268
269 let issuer = format!("CN={issuer_hostname}")
271 .parse()
272 .map_err(X509CertError::InvalidHostname)?;
273 let subject: x509_cert::name::Name = format!("CN={subject_hostname}")
274 .parse()
275 .map_err(X509CertError::InvalidHostname)?;
276
277 let self_signed_profile = Profile::Leaf {
278 issuer,
279 enable_key_agreement: true,
280 enable_key_encipherment: true,
281 include_subject_key_identifier: true,
282 };
283 let serial_number = random_serial_number(rng)?;
284 let (validity, expiration) = cert_validity(now, TLS_CERT_LIFETIME_DAYS)?;
285 let spki = SubjectPublicKeyInfo::from_key(public_key)?;
286
287 let builder = CertificateBuilder::new(
288 self_signed_profile,
289 serial_number,
290 validity,
291 subject,
292 spki,
293 &issuer_private_key,
294 )?;
295
296 let cert = builder.build::<ecdsa::der::Signature<_>>()?;
297
298 let mut certificate_der = Vec::new();
299 let _ignore_length: x509_cert::der::Length = cert
300 .encode_to_vec(&mut certificate_der)
301 .map_err(X509CertError::CouldNotEncode)?;
302
303 let sha256_digest = tor_llcrypto::d::Sha256::digest(&certificate_der).into();
304 let certificates = vec![certificate_der];
305
306 Ok(TlsKeyAndCert {
307 certificates,
308 private_key,
309 sha256_digest,
310 expiration,
311 })
312 }
313}
314
315fn cert_validity(
323 now: SystemTime,
324 lifetime_days: u32,
325) -> Result<(Validity, SystemTime), X509CertError> {
326 const ONE_DAY: Duration = Duration::new(86400, 0);
327
328 let start_of_day_containing = |when| -> Result<_, X509CertError> {
329 let dt = DateTime::from_system_time(when)
330 .map_err(into_internal!("Couldn't represent time as a DER DateTime"))?;
331 let dt = DateTime::new(dt.year(), dt.month(), dt.day(), 0, 0, 0)
332 .map_err(into_internal!("Couldn't construct DER DateTime"))?;
333 Ok(x509_cert::time::Time::GeneralTime(
334 GeneralizedTime::from_date_time(dt),
335 ))
336 };
337
338 let start_on_day = now - ONE_DAY;
339 let end_on_day = start_on_day + ONE_DAY * lifetime_days;
340
341 let validity = Validity {
342 not_before: start_of_day_containing(start_on_day)?,
343 not_after: start_of_day_containing(end_on_day)?,
344 };
345 let expiration = validity.not_after.into();
346 Ok((validity, expiration))
347}
348
349fn random_serial_number<Rng: CryptoRng>(rng: &mut Rng) -> Result<SerialNumber, X509CertError> {
351 const SER_NUMBER_LEN: usize = 16;
352 let mut buf = [0; SER_NUMBER_LEN];
353 rng.fill_bytes(&mut buf[..]);
354 Ok(SerialNumber::new(&buf[..]).map_err(into_internal!("Couldn't construct serial number!"))?)
355}
356
357#[derive(Clone, Debug, thiserror::Error)]
359#[non_exhaustive]
360pub enum X509CertError {
361 #[error("Provided signing key not valid: {0}")]
363 InvalidSigningKey(String),
364
365 #[error("Couldn't use provided key as a subject")]
367 SubjectKeyError(#[from] x509_cert::spki::Error),
368
369 #[error("Unable to set hostname when creating certificate")]
372 InvalidHostname(#[source] x509_cert::der::Error),
373
374 #[error("Unable to build certificate")]
376 CouldNotBuild(#[source] Arc<x509_cert::builder::Error>),
377
378 #[error("Unable to encode certificate")]
380 CouldNotEncode(#[source] x509_cert::der::Error),
381
382 #[error("Unable to format key as PKCS8")]
384 CouldNotFormatPkcs8(#[source] p256::pkcs8::Error),
385
386 #[error("Internal error while creating certificate")]
388 Bug(#[from] tor_error::Bug),
389}
390
391impl From<x509_cert::builder::Error> for X509CertError {
392 fn from(value: x509_cert::builder::Error) -> Self {
393 X509CertError::CouldNotBuild(Arc::new(value))
394 }
395}
396
397#[cfg(test)]
398mod test {
399 #![allow(clippy::bool_assert_comparison)]
401 #![allow(clippy::clone_on_copy)]
402 #![allow(clippy::dbg_macro)]
403 #![allow(clippy::mixed_attributes_style)]
404 #![allow(clippy::print_stderr)]
405 #![allow(clippy::print_stdout)]
406 #![allow(clippy::single_char_pattern)]
407 #![allow(clippy::unwrap_used)]
408 #![allow(clippy::unchecked_time_subtraction)]
409 #![allow(clippy::useless_vec)]
410 #![allow(clippy::needless_pass_by_value)]
411 #![allow(clippy::string_slice)] use super::*;
415 use tor_basic_utils::test_rng::testing_rng;
416 use web_time_compat::SystemTimeExt;
417
418 #[test]
419 fn identity_cert_generation() {
420 let mut rng = testing_rng();
421 let keypair = RsaKeypair::generate(&mut rng).unwrap();
422 let cert = create_legacy_rsa_id_cert(
423 &mut rng,
424 SystemTime::get(),
425 "www.house-of-pancakes.example.com",
426 &keypair,
427 )
428 .unwrap();
429
430 let key_extracted = tor_llcrypto::util::x509_extract_rsa_subject_kludge(&cert[..]).unwrap();
431 assert_eq!(key_extracted, keypair.to_public_key());
432
433 }
438
439 #[test]
440 fn tls_cert_info() {
441 let mut rng = testing_rng();
442 let certified = TlsKeyAndCert::create(
443 &mut rng,
444 SystemTime::get(),
445 "foo.example.com",
446 "bar.example.com",
447 )
448 .unwrap();
449 dbg!(certified);
450 }
451}