1use crate::{NormalizedHost, ProxyInstanceId};
4use base64::Engine as _;
5use base64::engine::general_purpose::STANDARD;
6use hns_dane::extract_spki_der;
7use rcgen::{
8 BasicConstraints, CertificateParams, DistinguishedName, DnType, IsCa, Issuer, KeyPair,
9 KeyUsagePurpose, PublicKeyData,
10};
11use ring::digest::{SHA1_FOR_LEGACY_USE_ONLY, SHA256, digest};
12use rustls::ServerConfig;
13use rustls::crypto::ring::sign::any_ecdsa_type;
14use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
15use rustls::server::{ClientHello, ResolvesServerCert};
16use rustls::sign::CertifiedKey;
17use std::collections::HashMap;
18use std::fmt;
19use std::sync::atomic::{AtomicBool, Ordering};
20use std::sync::{Arc, Mutex, MutexGuard};
21use subtle::ConstantTimeEq;
22use thiserror::Error;
23use time::{Duration, OffsetDateTime};
24use zeroize::Zeroizing;
25
26const MAX_CACHED_IDENTITIES: usize = 128;
27const MAX_PRESENTED_CERTIFICATE_DER_BYTES: usize = 64 * 1024;
28const MAX_LOCAL_CA_PRIVATE_KEY_DER_BYTES: usize = 16 * 1024;
29const LOCAL_LEAF_VALIDITY_DAYS: i64 = 90;
30const LOCAL_CA_VALIDITY_DAYS: i64 = 3_650;
31const LOCAL_CA_COMMON_NAME: &str = "HNS DANE Browser Local CA";
32
33#[derive(Clone)]
34pub struct LocalCertificateAuthority {
35 inner: Arc<LocalCertificateAuthorityInner>,
36}
37
38struct LocalCertificateAuthorityInner {
39 issuer: Issuer<'static, KeyPair>,
40 certificate_der: Vec<u8>,
41 certificate_sha1: [u8; 20],
42 certificate_sha256: [u8; 32],
43}
44
45pub struct GeneratedLocalCertificateAuthority {
46 authority: LocalCertificateAuthority,
47 private_key_der: Zeroizing<Vec<u8>>,
48}
49
50impl GeneratedLocalCertificateAuthority {
51 pub fn authority(&self) -> &LocalCertificateAuthority {
52 &self.authority
53 }
54
55 pub fn private_key_der(&self) -> &[u8] {
58 &self.private_key_der
59 }
60}
61
62impl fmt::Debug for GeneratedLocalCertificateAuthority {
63 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
64 formatter
65 .debug_struct("GeneratedLocalCertificateAuthority")
66 .field("authority", &self.authority)
67 .field("private_key_der", &"[REDACTED]")
68 .finish()
69 }
70}
71
72#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
73pub enum LocalCertificateAuthorityError {
74 #[error("local CA certificate is empty or exceeds the size bound")]
75 InvalidCertificateSize,
76 #[error("local CA private key is empty or exceeds the size bound")]
77 InvalidPrivateKeySize,
78 #[error("unable to generate the local CA")]
79 Generation,
80 #[error("unable to parse the local CA private key")]
81 InvalidPrivateKey,
82 #[error("unable to parse the local CA certificate")]
83 InvalidCertificate,
84 #[error("the local CA certificate does not match its private key")]
85 KeyMismatch,
86}
87
88impl LocalCertificateAuthority {
89 pub fn generate() -> Result<GeneratedLocalCertificateAuthority, LocalCertificateAuthorityError>
90 {
91 let key = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256)
92 .map_err(|_| LocalCertificateAuthorityError::Generation)?;
93 let private_key_der = Zeroizing::new(key.serialize_der());
94 let params = local_ca_params();
95 let certificate = params
96 .self_signed(&key)
97 .map_err(|_| LocalCertificateAuthorityError::Generation)?;
98 let certificate_der = certificate.der().as_ref().to_vec();
99 let authority = Self::from_parts(params, key, certificate_der)?;
100 Ok(GeneratedLocalCertificateAuthority {
101 authority,
102 private_key_der,
103 })
104 }
105
106 pub fn from_der(
107 certificate_der: Vec<u8>,
108 private_key_der: &[u8],
109 ) -> Result<Self, LocalCertificateAuthorityError> {
110 validate_ca_material_sizes(&certificate_der, private_key_der)?;
111 let private_key = rustls::pki_types::PrivatePkcs8KeyDer::from(private_key_der);
112 let key =
113 KeyPair::from_pkcs8_der_and_sign_algo(&private_key, &rcgen::PKCS_ECDSA_P256_SHA256)
114 .map_err(|_| LocalCertificateAuthorityError::InvalidPrivateKey)?;
115 let certificate_spki = extract_spki_der(&certificate_der)
116 .map_err(|_| LocalCertificateAuthorityError::InvalidCertificate)?;
117 let key_spki = key.subject_public_key_info();
118 if certificate_spki.len() != key_spki.len()
119 || !bool::from(certificate_spki.ct_eq(&key_spki))
120 {
121 return Err(LocalCertificateAuthorityError::KeyMismatch);
122 }
123 let certificate = rustls::pki_types::CertificateDer::from(certificate_der.clone());
124 let issuer = Issuer::from_ca_cert_der(&certificate, key)
125 .map_err(|_| LocalCertificateAuthorityError::InvalidCertificate)?;
126 Ok(Self::from_issuer(issuer, certificate_der))
127 }
128
129 pub fn certificate_der(&self) -> &[u8] {
130 &self.inner.certificate_der
131 }
132
133 pub fn certificate_pem(&self) -> String {
134 let encoded = STANDARD.encode(self.certificate_der());
135 let mut pem = String::from("-----BEGIN CERTIFICATE-----\n");
136 for (index, character) in encoded.chars().enumerate() {
137 pem.push(character);
138 if (index + 1) % 64 == 0 {
139 pem.push('\n');
140 }
141 }
142 if !encoded.len().is_multiple_of(64) {
143 pem.push('\n');
144 }
145 pem.push_str("-----END CERTIFICATE-----\n");
146 pem
147 }
148
149 pub fn certificate_sha256_hex(&self) -> String {
150 self.inner
151 .certificate_sha256
152 .iter()
153 .map(|byte| format!("{byte:02x}"))
154 .collect()
155 }
156
157 pub fn certificate_sha1_hex(&self) -> String {
160 self.inner
161 .certificate_sha1
162 .iter()
163 .map(|byte| format!("{byte:02x}"))
164 .collect()
165 }
166
167 fn from_parts(
168 params: CertificateParams,
169 key: KeyPair,
170 certificate_der: Vec<u8>,
171 ) -> Result<Self, LocalCertificateAuthorityError> {
172 validate_ca_material_sizes(&certificate_der, key.serialized_der())?;
173 Ok(Self::from_issuer(Issuer::new(params, key), certificate_der))
174 }
175
176 fn from_issuer(issuer: Issuer<'static, KeyPair>, certificate_der: Vec<u8>) -> Self {
177 let sha1_fingerprint = digest(&SHA1_FOR_LEGACY_USE_ONLY, &certificate_der);
178 let mut certificate_sha1 = [0_u8; 20];
179 certificate_sha1.copy_from_slice(sha1_fingerprint.as_ref());
180 let fingerprint = digest(&SHA256, &certificate_der);
181 let mut certificate_sha256 = [0_u8; 32];
182 certificate_sha256.copy_from_slice(fingerprint.as_ref());
183 Self {
184 inner: Arc::new(LocalCertificateAuthorityInner {
185 issuer,
186 certificate_der,
187 certificate_sha1,
188 certificate_sha256,
189 }),
190 }
191 }
192
193 fn issuer(&self) -> &Issuer<'static, KeyPair> {
194 &self.inner.issuer
195 }
196}
197
198impl PartialEq for LocalCertificateAuthority {
199 fn eq(&self, other: &Self) -> bool {
200 bool::from(
201 self.inner
202 .certificate_sha256
203 .ct_eq(&other.inner.certificate_sha256),
204 )
205 }
206}
207
208impl Eq for LocalCertificateAuthority {}
209
210impl fmt::Debug for LocalCertificateAuthority {
211 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
212 formatter
213 .debug_struct("LocalCertificateAuthority")
214 .field("certificate_sha256", &"[REDACTED]")
215 .finish()
216 }
217}
218
219fn validate_ca_material_sizes(
220 certificate_der: &[u8],
221 private_key_der: &[u8],
222) -> Result<(), LocalCertificateAuthorityError> {
223 if certificate_der.is_empty() || certificate_der.len() > MAX_PRESENTED_CERTIFICATE_DER_BYTES {
224 return Err(LocalCertificateAuthorityError::InvalidCertificateSize);
225 }
226 if private_key_der.is_empty() || private_key_der.len() > MAX_LOCAL_CA_PRIVATE_KEY_DER_BYTES {
227 return Err(LocalCertificateAuthorityError::InvalidPrivateKeySize);
228 }
229 Ok(())
230}
231
232fn local_ca_params() -> CertificateParams {
233 let now = OffsetDateTime::now_utc();
234 let mut distinguished_name = DistinguishedName::new();
235 distinguished_name.push(DnType::CommonName, LOCAL_CA_COMMON_NAME);
236 let mut params = CertificateParams::default();
237 params.not_before = now - Duration::days(1);
238 params.not_after = now + Duration::days(LOCAL_CA_VALIDITY_DAYS);
239 params.distinguished_name = distinguished_name;
240 params.is_ca = IsCa::Ca(BasicConstraints::Constrained(0));
241 params.key_usages = vec![
242 KeyUsagePurpose::DigitalSignature,
243 KeyUsagePurpose::KeyCertSign,
244 KeyUsagePurpose::CrlSign,
245 ];
246 params
247}
248
249#[derive(Clone, Copy)]
254pub struct CertificateSha256([u8; 32]);
255
256impl CertificateSha256 {
257 pub(crate) fn matches_der(&self, certificate_der: &[u8]) -> bool {
260 if certificate_der.len() > MAX_PRESENTED_CERTIFICATE_DER_BYTES {
261 return false;
262 }
263 let candidate = digest(&SHA256, certificate_der);
264 self.0.ct_eq(candidate.as_ref()).into()
265 }
266
267 fn from_der(certificate_der: &[u8]) -> Self {
268 let fingerprint = digest(&SHA256, certificate_der);
269 let mut bytes = [0_u8; 32];
270 bytes.copy_from_slice(fingerprint.as_ref());
271 Self(bytes)
272 }
273}
274
275impl PartialEq for CertificateSha256 {
276 fn eq(&self, other: &Self) -> bool {
277 self.0.ct_eq(&other.0).into()
278 }
279}
280
281impl Eq for CertificateSha256 {}
282
283impl fmt::Debug for CertificateSha256 {
284 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
285 formatter.write_str("CertificateSha256([REDACTED])")
286 }
287}
288
289#[derive(Clone, Eq, PartialEq)]
291pub struct LocalCertificatePin {
292 instance: ProxyInstanceId,
293 host: NormalizedHost,
294 certificate_sha256: CertificateSha256,
295}
296
297impl LocalCertificatePin {
298 pub fn instance(&self) -> &ProxyInstanceId {
300 &self.instance
301 }
302
303 pub fn host(&self) -> &NormalizedHost {
305 &self.host
306 }
307
308 pub fn certificate_sha256(&self) -> &CertificateSha256 {
310 &self.certificate_sha256
311 }
312
313 pub(crate) fn matches_der(&self, certificate_der: &[u8]) -> bool {
316 self.certificate_sha256.matches_der(certificate_der)
317 }
318}
319
320impl fmt::Debug for LocalCertificatePin {
321 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
322 formatter
323 .debug_struct("LocalCertificatePin")
324 .field("instance", &"[REDACTED]")
325 .field("host", &"[REDACTED]")
326 .field("certificate_sha256", &"[REDACTED]")
327 .finish()
328 }
329}
330
331#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
332pub(crate) enum CertificateError {
333 #[error("the local TLS identity store is inactive")]
334 Inactive,
335 #[error("the local TLS identity cache is full of active leases")]
336 CapacityExhausted,
337 #[error("the local TLS identity store is unavailable")]
338 StoreUnavailable,
339 #[error("unable to generate a local TLS certificate")]
340 CertificateGeneration,
341 #[error("unable to load the generated local TLS signing key")]
342 SigningKey,
343 #[error("unable to configure the local TLS server")]
344 ServerConfiguration,
345}
346
347pub(crate) struct IdentityLease {
349 identity: Arc<LocalTlsIdentity>,
350}
351
352impl IdentityLease {
353 pub(crate) fn server_config(&self) -> Arc<ServerConfig> {
354 Arc::clone(&self.identity.server_config)
355 }
356
357 pub(crate) fn pin(&self) -> &LocalCertificatePin {
358 &self.identity.pin
359 }
360}
361
362impl fmt::Debug for IdentityLease {
363 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
364 formatter.write_str("IdentityLease([REDACTED])")
365 }
366}
367
368struct LocalTlsIdentity {
369 server_config: Arc<ServerConfig>,
370 pin: LocalCertificatePin,
371 #[cfg(test)]
374 certificate_der: Vec<u8>,
375}
376
377struct CacheEntry {
378 identity: Arc<LocalTlsIdentity>,
379 last_access: u128,
380}
381
382#[derive(Default)]
383struct StoreState {
384 entries: HashMap<NormalizedHost, CacheEntry>,
385 access_counter: u128,
386}
387
388impl StoreState {
389 fn next_access(&mut self) -> u128 {
390 if self.access_counter == u128::MAX {
391 let mut oldest_first: Vec<_> = self
392 .entries
393 .iter()
394 .map(|(host, entry)| (host.clone(), entry.last_access))
395 .collect();
396 oldest_first.sort_by_key(|(_, last_access)| *last_access);
397 for (rank, (host, _)) in oldest_first.into_iter().enumerate() {
398 if let Some(entry) = self.entries.get_mut(&host) {
399 entry.last_access = rank as u128;
400 }
401 }
402 self.access_counter = self.entries.len() as u128;
403 }
404 self.access_counter += 1;
405 self.access_counter
406 }
407}
408
409pub(crate) struct LocalTlsIdentityStore {
411 instance: ProxyInstanceId,
412 certificate_authority: Option<LocalCertificateAuthority>,
413 active: Arc<AtomicBool>,
414 state: Mutex<StoreState>,
415}
416
417impl LocalTlsIdentityStore {
418 #[cfg(test)]
419 pub(crate) fn new(instance: ProxyInstanceId) -> Self {
420 Self::with_certificate_authority(instance, None)
421 }
422
423 pub(crate) fn with_certificate_authority(
424 instance: ProxyInstanceId,
425 certificate_authority: Option<LocalCertificateAuthority>,
426 ) -> Self {
427 Self {
428 instance,
429 certificate_authority,
430 active: Arc::new(AtomicBool::new(true)),
431 state: Mutex::new(StoreState::default()),
432 }
433 }
434
435 pub(crate) fn prepare(&self, host: &NormalizedHost) -> Result<IdentityLease, CertificateError> {
436 if !self.active.load(Ordering::Acquire) {
437 return Err(CertificateError::Inactive);
438 }
439 let mut state = self
440 .state
441 .lock()
442 .map_err(|_| CertificateError::StoreUnavailable)?;
443 if !self.active.load(Ordering::Acquire) {
444 return Err(CertificateError::Inactive);
445 }
446
447 let access = state.next_access();
448 if let Some(entry) = state.entries.get_mut(host) {
449 entry.last_access = access;
450 if !self.active.load(Ordering::Acquire) {
451 return Err(CertificateError::Inactive);
452 }
453 return Ok(IdentityLease {
454 identity: Arc::clone(&entry.identity),
455 });
456 }
457
458 let eviction = if state.entries.len() < MAX_CACHED_IDENTITIES {
459 None
460 } else {
461 Some(
462 state
463 .entries
464 .iter()
465 .filter(|(_, entry)| Arc::strong_count(&entry.identity) == 1)
466 .min_by_key(|(_, entry)| entry.last_access)
467 .map(|(candidate, _)| candidate.clone())
468 .ok_or(CertificateError::CapacityExhausted)?,
469 )
470 };
471
472 let identity = Arc::new(generate_identity(
476 self.instance.clone(),
477 host.clone(),
478 Arc::clone(&self.active),
479 self.certificate_authority.as_ref(),
480 )?);
481 if !self.active.load(Ordering::Acquire) {
482 return Err(CertificateError::Inactive);
483 }
484 if let Some(eviction) = eviction {
485 state.entries.remove(&eviction);
486 }
487 state.entries.insert(
488 host.clone(),
489 CacheEntry {
490 identity: Arc::clone(&identity),
491 last_access: access,
492 },
493 );
494 if !self.active.load(Ordering::Acquire) {
495 state.entries.remove(host);
496 return Err(CertificateError::Inactive);
497 }
498 Ok(IdentityLease { identity })
499 }
500
501 pub(crate) fn pin(&self, host: &NormalizedHost) -> Option<LocalCertificatePin> {
502 if !self.active.load(Ordering::Acquire) {
503 return None;
504 }
505 let mut state = self.state.lock().ok()?;
506 if !self.active.load(Ordering::Acquire) {
507 return None;
508 }
509 let access = state.next_access();
510 let entry = state.entries.get_mut(host)?;
511 entry.last_access = access;
512 Some(entry.identity.pin.clone())
513 }
514
515 pub(crate) fn matches_der(&self, host: &NormalizedHost, certificate_der: &[u8]) -> bool {
516 if certificate_der.len() > MAX_PRESENTED_CERTIFICATE_DER_BYTES {
517 return false;
518 }
519 self.pin(host)
520 .is_some_and(|pin| pin.matches_der(certificate_der))
521 }
522
523 pub(crate) fn deactivate(&self) {
524 self.active.store(false, Ordering::Release);
527 let mut state = lock_recover(&self.state);
528 state.entries.clear();
529 }
530}
531
532impl fmt::Debug for LocalTlsIdentityStore {
533 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
534 let entry_count = self
535 .state
536 .lock()
537 .map(|state| state.entries.len())
538 .unwrap_or_default();
539 formatter
540 .debug_struct("LocalTlsIdentityStore")
541 .field("instance", &"[REDACTED]")
542 .field(
543 "certificate_authority",
544 &self.certificate_authority.as_ref().map(|_| "[REDACTED]"),
545 )
546 .field("active", &self.active.load(Ordering::Acquire))
547 .field("entry_count", &entry_count)
548 .finish()
549 }
550}
551
552impl Drop for LocalTlsIdentityStore {
553 fn drop(&mut self) {
554 self.deactivate();
555 }
556}
557
558#[derive(Clone)]
559struct ExactSniResolver {
560 expected_host: NormalizedHost,
561 certified_key: Arc<CertifiedKey>,
562 active: Arc<AtomicBool>,
563}
564
565impl ExactSniResolver {
566 fn allows(&self, presented_sni: Option<&str>) -> bool {
567 self.active.load(Ordering::Acquire)
568 && presented_sni
569 .and_then(|name| NormalizedHost::parse(name).ok())
570 .is_some_and(|name| name == self.expected_host)
571 }
572}
573
574impl fmt::Debug for ExactSniResolver {
575 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
576 formatter
577 .debug_struct("ExactSniResolver")
578 .field("expected_host", &"[REDACTED]")
579 .field("active", &self.active.load(Ordering::Acquire))
580 .finish()
581 }
582}
583
584impl ResolvesServerCert for ExactSniResolver {
585 fn resolve(&self, client_hello: ClientHello<'_>) -> Option<Arc<CertifiedKey>> {
586 self.allows(client_hello.server_name())
587 .then(|| Arc::clone(&self.certified_key))
588 }
589}
590
591fn generate_identity(
592 instance: ProxyInstanceId,
593 host: NormalizedHost,
594 active: Arc<AtomicBool>,
595 certificate_authority: Option<&LocalCertificateAuthority>,
596) -> Result<LocalTlsIdentity, CertificateError> {
597 let mut params = rcgen::CertificateParams::new(vec![host.as_str().to_owned()])
598 .map_err(|_| CertificateError::CertificateGeneration)?;
599 let now = OffsetDateTime::now_utc();
600 params.not_before = now - Duration::minutes(5);
601 params.not_after = now + Duration::days(LOCAL_LEAF_VALIDITY_DAYS);
602 params.key_usages = vec![rcgen::KeyUsagePurpose::DigitalSignature];
603 params.extended_key_usages = vec![rcgen::ExtendedKeyUsagePurpose::ServerAuth];
604
605 let key_pair = Zeroizing::new(
609 rcgen::KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256)
610 .map_err(|_| CertificateError::CertificateGeneration)?,
611 );
612 let certificate = if let Some(certificate_authority) = certificate_authority {
613 params.signed_by(&*key_pair, certificate_authority.issuer())
614 } else {
615 params.self_signed(&*key_pair)
616 }
617 .map_err(|_| CertificateError::CertificateGeneration)?;
618 let certificate_der = certificate.der().as_ref().to_vec();
619 if certificate_der.len() > MAX_PRESENTED_CERTIFICATE_DER_BYTES {
620 return Err(CertificateError::CertificateGeneration);
621 }
622
623 let private_key_der = Zeroizing::new(key_pair.serialize_der());
626 let borrowed_key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(private_key_der.as_slice()));
627 let signing_key = any_ecdsa_type(&borrowed_key).map_err(|_| CertificateError::SigningKey)?;
628 let certified_key = Arc::new(CertifiedKey::new(
629 vec![CertificateDer::from(certificate_der.clone())],
630 signing_key,
631 ));
632 certified_key
633 .keys_match()
634 .map_err(|_| CertificateError::SigningKey)?;
635
636 let resolver = Arc::new(ExactSniResolver {
637 expected_host: host.clone(),
638 certified_key,
639 active,
640 });
641 let mut server_config =
642 ServerConfig::builder_with_provider(Arc::new(rustls::crypto::ring::default_provider()))
643 .with_protocol_versions(&[&rustls::version::TLS13, &rustls::version::TLS12])
644 .map_err(|_| CertificateError::ServerConfiguration)?
645 .with_no_client_auth()
646 .with_cert_resolver(resolver);
647 server_config.alpn_protocols = vec![b"http/1.1".to_vec()];
648 server_config.session_storage = Arc::new(rustls::server::NoServerSessionStorage {});
653 server_config.send_tls13_tickets = 0;
654
655 let pin = LocalCertificatePin {
656 instance,
657 host,
658 certificate_sha256: CertificateSha256::from_der(&certificate_der),
659 };
660 Ok(LocalTlsIdentity {
661 server_config: Arc::new(server_config),
662 pin,
663 #[cfg(test)]
664 certificate_der,
665 })
666}
667
668fn lock_recover<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
669 mutex
670 .lock()
671 .unwrap_or_else(|poisoned| poisoned.into_inner())
672}
673
674#[cfg(test)]
675mod tests {
676 use super::*;
677 use crate::ProxySessionId;
678 use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
679 use rustls::pki_types::{ServerName, UnixTime};
680 use rustls::{
681 ClientConfig, ClientConnection, DigitallySignedStruct, Error as RustlsError,
682 ServerConnection, SignatureScheme, SupportedProtocolVersion,
683 };
684 use std::sync::Barrier;
685 use std::thread;
686
687 fn instance(generation: u64) -> ProxyInstanceId {
688 ProxyInstanceId::new(ProxySessionId::generate().unwrap(), generation)
689 }
690
691 fn host(name: &str) -> NormalizedHost {
692 NormalizedHost::parse(name).unwrap()
693 }
694
695 fn entry_count(store: &LocalTlsIdentityStore) -> usize {
696 lock_recover(&store.state).entries.len()
697 }
698
699 #[test]
700 fn identities_are_fresh_across_stores_and_reused_within_one_store() {
701 let shared_instance = instance(7);
702 let first = LocalTlsIdentityStore::new(shared_instance.clone());
703 let second = LocalTlsIdentityStore::new(shared_instance);
704 let canonical = host("WELCOME.");
705 let equivalent = host("welcome");
706
707 let first_lease = first.prepare(&canonical).unwrap();
708 let reused_lease = first.prepare(&equivalent).unwrap();
709 let second_lease = second.prepare(&equivalent).unwrap();
710
711 assert!(Arc::ptr_eq(&first_lease.identity, &reused_lease.identity));
712 assert_eq!(first_lease.pin(), reused_lease.pin());
713 assert_ne!(
714 first_lease.pin().certificate_sha256(),
715 second_lease.pin().certificate_sha256()
716 );
717 assert_eq!(first_lease.pin().host(), &equivalent);
718 assert_eq!(entry_count(&first), 1);
719 }
720
721 #[test]
722 fn pins_match_only_the_exact_host_certificate_and_active_generation() {
723 let store = LocalTlsIdentityStore::new(instance(11));
724 let welcome = host("welcome");
725 let subdomain = host("www.welcome");
726 let lease = store.prepare(&welcome).unwrap();
727 let certificate_der = lease.identity.certificate_der.clone();
728
729 assert!(lease.pin().matches_der(&certificate_der));
730 assert!(store.matches_der(&welcome, &certificate_der));
731 assert!(!store.matches_der(&subdomain, &certificate_der));
732 assert!(!store.matches_der(&welcome, b"not the certificate"));
733 assert!(!store.matches_der(
734 &welcome,
735 &vec![0_u8; MAX_PRESENTED_CERTIFICATE_DER_BYTES + 1]
736 ));
737
738 store.deactivate();
739 assert_eq!(entry_count(&store), 0);
740 assert!(store.pin(&welcome).is_none());
741 assert!(!store.matches_der(&welcome, &certificate_der));
742 assert!(matches!(
743 store.prepare(&welcome),
744 Err(CertificateError::Inactive)
745 ));
746 }
747
748 #[test]
749 fn local_ca_material_round_trips_and_rejects_a_mismatched_key() {
750 let generated = LocalCertificateAuthority::generate().unwrap();
751 let authority = generated.authority().clone();
752 let reloaded = LocalCertificateAuthority::from_der(
753 authority.certificate_der().to_vec(),
754 generated.private_key_der(),
755 )
756 .unwrap();
757 assert_eq!(authority, reloaded);
758 assert_eq!(
759 authority.certificate_sha256_hex(),
760 reloaded.certificate_sha256_hex()
761 );
762 assert!(
763 authority
764 .certificate_pem()
765 .starts_with("-----BEGIN CERTIFICATE-----\n")
766 );
767 assert!(
768 authority
769 .certificate_pem()
770 .ends_with("-----END CERTIFICATE-----\n")
771 );
772
773 let other = LocalCertificateAuthority::generate().unwrap();
774 assert_eq!(
775 LocalCertificateAuthority::from_der(
776 authority.certificate_der().to_vec(),
777 other.private_key_der(),
778 )
779 .unwrap_err(),
780 LocalCertificateAuthorityError::KeyMismatch
781 );
782 }
783
784 #[test]
785 fn ca_signed_leaf_validates_for_only_its_exact_host() {
786 let generated = LocalCertificateAuthority::generate().unwrap();
787 let authority = generated.authority().clone();
788 let store = LocalTlsIdentityStore::with_certificate_authority(
789 instance(19),
790 Some(authority.clone()),
791 );
792 let lease = store.prepare(&host("welcome")).unwrap();
793
794 assert_eq!(
795 trusted_handshake(
796 lease.server_config(),
797 "welcome",
798 &rustls::version::TLS13,
799 &authority,
800 )
801 .unwrap(),
802 Some(b"http/1.1".to_vec())
803 );
804 assert!(
805 trusted_handshake(
806 lease.server_config(),
807 "other.welcome",
808 &rustls::version::TLS13,
809 &authority,
810 )
811 .is_err()
812 );
813 }
814
815 #[test]
816 fn diagnostics_redact_hosts_generation_tokens_and_fingerprints() {
817 let store = LocalTlsIdentityStore::new(instance(23));
818 let private_host = host("private-history.welcome");
819 let lease = store.prepare(&private_host).unwrap();
820 let pin_debug = format!("{:?}", lease.pin());
821 let digest_debug = format!("{:?}", lease.pin().certificate_sha256());
822 let lease_debug = format!("{lease:?}");
823 let store_debug = format!("{store:?}");
824
825 for diagnostic in [pin_debug, digest_debug, lease_debug, store_debug] {
826 assert!(!diagnostic.contains(private_host.as_str()));
827 assert!(!diagnostic.contains(store.instance.session().as_str()));
828 assert!(diagnostic.contains("REDACTED") || diagnostic.contains("entry_count"));
829 }
830 }
831
832 #[test]
833 fn cache_evicts_the_least_recent_idle_identity_only() {
834 let store = LocalTlsIdentityStore::new(instance(31));
835 let hosts: Vec<_> = (0..MAX_CACHED_IDENTITIES)
836 .map(|index| host(&format!("host-{index}.welcome")))
837 .collect();
838 for candidate in &hosts {
839 drop(store.prepare(candidate).unwrap());
840 }
841 assert_eq!(entry_count(&store), MAX_CACHED_IDENTITIES);
842
843 drop(store.prepare(&hosts[0]).unwrap());
845 let replacement = host("replacement.welcome");
846 drop(store.prepare(&replacement).unwrap());
847
848 assert_eq!(entry_count(&store), MAX_CACHED_IDENTITIES);
849 assert!(store.pin(&hosts[0]).is_some());
850 assert!(store.pin(&hosts[1]).is_none());
851 assert!(store.pin(&replacement).is_some());
852 }
853
854 #[test]
855 fn cache_fails_closed_when_every_identity_is_leased() {
856 let store = LocalTlsIdentityStore::new(instance(37));
857 let hosts: Vec<_> = (0..MAX_CACHED_IDENTITIES)
858 .map(|index| host(&format!("leased-{index}.welcome")))
859 .collect();
860 let mut leases: Vec<_> = hosts
861 .iter()
862 .map(|candidate| store.prepare(candidate).unwrap())
863 .collect();
864 let extra = host("extra.welcome");
865
866 assert!(matches!(
867 store.prepare(&extra),
868 Err(CertificateError::CapacityExhausted)
869 ));
870 assert_eq!(entry_count(&store), MAX_CACHED_IDENTITIES);
871
872 drop(leases.remove(0));
873 assert!(store.prepare(&extra).is_ok());
874 assert_eq!(entry_count(&store), MAX_CACHED_IDENTITIES);
875 }
876
877 #[test]
878 fn deactivate_fails_closed_for_prepare_and_pin_racing_the_state_lock() {
879 let store = Arc::new(LocalTlsIdentityStore::new(instance(38)));
880 let cached_host = host("cached.welcome");
881 let pending_host = host("pending.welcome");
882 let retained_lease = store.prepare(&cached_host).unwrap();
883 let cached_certificate_der = retained_lease.identity.certificate_der.clone();
884
885 let state = lock_recover(&store.state);
890 let lookup_gate = Arc::new(Barrier::new(3));
891 let prepare_thread = {
892 let store = Arc::clone(&store);
893 let lookup_gate = Arc::clone(&lookup_gate);
894 thread::spawn(move || {
895 lookup_gate.wait();
896 store
897 .prepare(&pending_host)
898 .map(|lease| lease.pin().clone())
899 })
900 };
901 let pin_thread = {
902 let store = Arc::clone(&store);
903 let lookup_gate = Arc::clone(&lookup_gate);
904 thread::spawn(move || {
905 lookup_gate.wait();
906 store.pin(&cached_host)
907 })
908 };
909 lookup_gate.wait();
910
911 let deactivate_thread = {
912 let store = Arc::clone(&store);
913 thread::spawn(move || store.deactivate())
914 };
915 while store.active.load(Ordering::Acquire) {
916 thread::yield_now();
917 }
918 drop(state);
919
920 assert!(matches!(
921 prepare_thread.join().unwrap(),
922 Err(CertificateError::Inactive)
923 ));
924 assert!(pin_thread.join().unwrap().is_none());
925 deactivate_thread.join().unwrap();
926
927 assert_eq!(entry_count(&store), 0);
928 assert!(store.pin(&host("cached.welcome")).is_none());
929 assert!(!store.matches_der(&host("cached.welcome"), &cached_certificate_der));
930 assert!(
931 handshake(
932 retained_lease.server_config(),
933 "cached.welcome",
934 &rustls::version::TLS13,
935 true,
936 )
937 .is_err()
938 );
939 }
940
941 #[test]
942 fn eviction_skips_a_retained_lru_lease_then_deactivation_revokes_it() {
943 let store = LocalTlsIdentityStore::new(instance(39));
944 let hosts: Vec<_> = (0..MAX_CACHED_IDENTITIES)
945 .map(|index| host(&format!("lease-aware-{index}.welcome")))
946 .collect();
947 let retained_lru = store.prepare(&hosts[0]).unwrap();
948 let retained_certificate_der = retained_lru.identity.certificate_der.clone();
949 for candidate in &hosts[1..] {
950 drop(store.prepare(candidate).unwrap());
951 }
952
953 let replacement = host("lease-aware-replacement.welcome");
954 drop(store.prepare(&replacement).unwrap());
955
956 assert_eq!(entry_count(&store), MAX_CACHED_IDENTITIES);
957 assert_eq!(store.pin(&hosts[0]).as_ref(), Some(retained_lru.pin()));
958 assert!(store.pin(&hosts[1]).is_none());
959 assert!(store.pin(&replacement).is_some());
960 assert!(store.matches_der(&hosts[0], &retained_certificate_der));
961
962 store.deactivate();
963
964 assert_eq!(entry_count(&store), 0);
965 assert!(retained_lru.pin().matches_der(&retained_certificate_der));
966 assert!(!store.matches_der(&hosts[0], &retained_certificate_der));
967 assert!(
968 handshake(
969 retained_lru.server_config(),
970 hosts[0].as_str(),
971 &rustls::version::TLS13,
972 true,
973 )
974 .is_err()
975 );
976 }
977
978 #[derive(Debug)]
979 struct AcceptCertificate;
980
981 impl ServerCertVerifier for AcceptCertificate {
982 fn verify_server_cert(
983 &self,
984 _end_entity: &CertificateDer<'_>,
985 _intermediates: &[CertificateDer<'_>],
986 _server_name: &ServerName<'_>,
987 _ocsp_response: &[u8],
988 _now: UnixTime,
989 ) -> Result<ServerCertVerified, RustlsError> {
990 Ok(ServerCertVerified::assertion())
991 }
992
993 fn verify_tls12_signature(
994 &self,
995 message: &[u8],
996 cert: &CertificateDer<'_>,
997 signature: &DigitallySignedStruct,
998 ) -> Result<HandshakeSignatureValid, RustlsError> {
999 rustls::crypto::verify_tls12_signature(
1000 message,
1001 cert,
1002 signature,
1003 &rustls::crypto::ring::default_provider().signature_verification_algorithms,
1004 )
1005 }
1006
1007 fn verify_tls13_signature(
1008 &self,
1009 message: &[u8],
1010 cert: &CertificateDer<'_>,
1011 signature: &DigitallySignedStruct,
1012 ) -> Result<HandshakeSignatureValid, RustlsError> {
1013 rustls::crypto::verify_tls13_signature(
1014 message,
1015 cert,
1016 signature,
1017 &rustls::crypto::ring::default_provider().signature_verification_algorithms,
1018 )
1019 }
1020
1021 fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
1022 rustls::crypto::ring::default_provider()
1023 .signature_verification_algorithms
1024 .supported_schemes()
1025 }
1026 }
1027
1028 fn handshake(
1029 server_config: Arc<ServerConfig>,
1030 sni: &str,
1031 version: &'static SupportedProtocolVersion,
1032 send_sni: bool,
1033 ) -> Result<Option<Vec<u8>>, RustlsError> {
1034 let mut client_config =
1035 ClientConfig::builder_with_provider(Arc::new(rustls::crypto::ring::default_provider()))
1036 .with_protocol_versions(&[version])
1037 .unwrap()
1038 .dangerous()
1039 .with_custom_certificate_verifier(Arc::new(AcceptCertificate))
1040 .with_no_client_auth();
1041 client_config.enable_sni = send_sni;
1042 client_config.alpn_protocols = vec![b"http/1.1".to_vec()];
1043 handshake_with_client_config(server_config, sni, Arc::new(client_config))
1044 }
1045
1046 fn trusted_handshake(
1047 server_config: Arc<ServerConfig>,
1048 sni: &str,
1049 version: &'static SupportedProtocolVersion,
1050 authority: &LocalCertificateAuthority,
1051 ) -> Result<Option<Vec<u8>>, RustlsError> {
1052 let mut roots = rustls::RootCertStore::empty();
1053 roots
1054 .add(CertificateDer::from(authority.certificate_der().to_vec()))
1055 .map_err(|error| RustlsError::General(error.to_string()))?;
1056 let mut client_config =
1057 ClientConfig::builder_with_provider(Arc::new(rustls::crypto::ring::default_provider()))
1058 .with_protocol_versions(&[version])
1059 .map_err(|error| RustlsError::General(error.to_string()))?
1060 .with_root_certificates(roots)
1061 .with_no_client_auth();
1062 client_config.alpn_protocols = vec![b"http/1.1".to_vec()];
1063 handshake_with_client_config(server_config, sni, Arc::new(client_config))
1064 }
1065
1066 fn handshake_with_client_config(
1067 server_config: Arc<ServerConfig>,
1068 sni: &str,
1069 client_config: Arc<ClientConfig>,
1070 ) -> Result<Option<Vec<u8>>, RustlsError> {
1071 let server_name = ServerName::try_from(sni.to_owned()).unwrap();
1072 let mut client = ClientConnection::new(client_config, server_name)?;
1073 let mut server = ServerConnection::new(server_config)?;
1074
1075 for _ in 0..32 {
1076 let mut client_wire = Vec::new();
1077 while client.wants_write() {
1078 client
1079 .write_tls(&mut client_wire)
1080 .map_err(|_| in_memory_io_error())?;
1081 }
1082 if !client_wire.is_empty() {
1083 server
1084 .read_tls(&mut client_wire.as_slice())
1085 .map_err(|_| in_memory_io_error())?;
1086 server.process_new_packets()?;
1087 }
1088
1089 let mut server_wire = Vec::new();
1090 while server.wants_write() {
1091 server
1092 .write_tls(&mut server_wire)
1093 .map_err(|_| in_memory_io_error())?;
1094 }
1095 if !server_wire.is_empty() {
1096 client
1097 .read_tls(&mut server_wire.as_slice())
1098 .map_err(|_| in_memory_io_error())?;
1099 client.process_new_packets()?;
1100 }
1101
1102 if !client.is_handshaking() && !server.is_handshaking() {
1103 return Ok(client.alpn_protocol().map(<[u8]>::to_vec));
1104 }
1105 }
1106 Err(RustlsError::General(
1107 "in-memory TLS handshake did not finish".to_owned(),
1108 ))
1109 }
1110
1111 fn in_memory_io_error() -> RustlsError {
1112 RustlsError::General("in-memory TLS handshake I/O failed".to_owned())
1113 }
1114
1115 #[test]
1116 fn resolver_requires_exact_canonical_sni_and_supports_tls12_tls13_http11() {
1117 let store = LocalTlsIdentityStore::new(instance(41));
1118 let welcome = host("welcome");
1119 let lease = store.prepare(&welcome).unwrap();
1120 assert!(!lease.server_config().session_storage.can_cache());
1121 assert_eq!(lease.server_config().send_tls13_tickets, 0);
1122
1123 for version in [&rustls::version::TLS12, &rustls::version::TLS13] {
1124 assert_eq!(
1125 handshake(lease.server_config(), "welcome", version, true).unwrap(),
1126 Some(b"http/1.1".to_vec())
1127 );
1128 }
1129 assert_eq!(
1130 handshake(
1131 lease.server_config(),
1132 "WELCOME.",
1133 &rustls::version::TLS13,
1134 true,
1135 )
1136 .unwrap(),
1137 Some(b"http/1.1".to_vec())
1138 );
1139 assert!(
1140 handshake(
1141 lease.server_config(),
1142 "other.welcome",
1143 &rustls::version::TLS13,
1144 true
1145 )
1146 .is_err()
1147 );
1148 assert!(
1149 handshake(
1150 lease.server_config(),
1151 "welcome",
1152 &rustls::version::TLS13,
1153 false
1154 )
1155 .is_err()
1156 );
1157
1158 store.deactivate();
1159 assert!(
1160 handshake(
1161 lease.server_config(),
1162 "welcome",
1163 &rustls::version::TLS13,
1164 true
1165 )
1166 .is_err()
1167 );
1168 }
1169}