Skip to main content

network_protocol/transport/
tls.rs

1//! # TLS Transport Layer
2//!
3//! This file is part of the Network Protocol project.
4//!
5//! It defines the TLS transport layer for secure network communication,
6//! particularly for external untrusted connections.
7//!
8//! The TLS transport layer provides a secure channel for communication
9//! using industry-standard TLS protocol, ensuring confidentiality,
10//! integrity, and authentication of the data transmitted.
11//!
12//! ## Responsibilities
13//! - Establish secure TLS connections
14//! - Handle TLS certificates and verification
15//! - Provide secure framed transport for higher protocol layers
16//! - Compatible with existing packet codec infrastructure
17
18use std::fs::File;
19use std::io::{self, BufReader, Seek, Write};
20use std::net::SocketAddr;
21use std::path::Path;
22use std::sync::Arc;
23
24use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
25use rustls::client::WebPkiServerVerifier;
26use rustls::crypto::{CryptoProvider, WebPkiSupportedAlgorithms};
27use rustls::pki_types::pem::PemObject;
28use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer, ServerName, UnixTime};
29use rustls::{ClientConfig, DigitallySignedStruct, RootCertStore, ServerConfig};
30use tokio::net::{TcpListener, TcpStream};
31use tokio_rustls::client::TlsStream as ClientTlsStream;
32use tokio_rustls::server::TlsStream as ServerTlsStream;
33use tokio_rustls::{TlsAcceptor, TlsConnector};
34use tokio_util::codec::Framed;
35use tracing::{debug, error, info, instrument, warn};
36
37use crate::core::codec::PacketCodec;
38use crate::core::packet::Packet;
39use crate::error::{ProtocolError, Result};
40use crate::utils::timeout::HANDSHAKE_TIMEOUT;
41use futures::{SinkExt, StreamExt};
42
43// Custom certificate verifiers.
44//
45// Both verifiers replace only the certificate *validation* step (chain building and
46// hostname checks). They still verify the handshake signature with the provider's
47// signature algorithms, which is what proves that the server holds the private key
48// for the certificate it presented. Without that check a pinned certificate, which is
49// public, could be replayed by any server.
50
51/// Accepts only a server certificate whose SHA-256 fingerprint matches the pin.
52#[derive(Debug)]
53struct CertificateFingerprint {
54    fingerprint: Vec<u8>,
55    supported_algs: WebPkiSupportedAlgorithms,
56}
57
58impl ServerCertVerifier for CertificateFingerprint {
59    fn verify_server_cert(
60        &self,
61        end_entity: &CertificateDer<'_>,
62        _intermediates: &[CertificateDer<'_>],
63        _server_name: &ServerName,
64        _ocsp_response: &[u8],
65        _now: UnixTime,
66    ) -> std::result::Result<ServerCertVerified, rustls::Error> {
67        use sha2::{Digest, Sha256};
68
69        let mut hasher = Sha256::new();
70        hasher.update(end_entity);
71        let hash = hasher.finalize();
72
73        if hash.as_slice() == self.fingerprint.as_slice() {
74            Ok(ServerCertVerified::assertion())
75        } else {
76            Err(rustls::Error::General(
77                "Pinned certificate hash mismatch".into(),
78            ))
79        }
80    }
81
82    fn verify_tls12_signature(
83        &self,
84        message: &[u8],
85        cert: &CertificateDer<'_>,
86        dss: &DigitallySignedStruct,
87    ) -> std::result::Result<HandshakeSignatureValid, rustls::Error> {
88        rustls::crypto::verify_tls12_signature(message, cert, dss, &self.supported_algs)
89    }
90
91    fn verify_tls13_signature(
92        &self,
93        message: &[u8],
94        cert: &CertificateDer<'_>,
95        dss: &DigitallySignedStruct,
96    ) -> std::result::Result<HandshakeSignatureValid, rustls::Error> {
97        rustls::crypto::verify_tls13_signature(message, cert, dss, &self.supported_algs)
98    }
99
100    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
101        self.supported_algs.supported_schemes()
102    }
103}
104
105/// Full CA and hostname validation through webpki, plus a pinned SHA-256 fingerprint
106/// of the end-entity certificate. Used when a pin is set without `insecure()`, so the
107/// pin narrows normal validation instead of replacing it.
108#[derive(Debug)]
109struct PinnedWebPkiVerifier {
110    inner: Arc<WebPkiServerVerifier>,
111    fingerprint: Vec<u8>,
112}
113
114impl ServerCertVerifier for PinnedWebPkiVerifier {
115    fn verify_server_cert(
116        &self,
117        end_entity: &CertificateDer<'_>,
118        intermediates: &[CertificateDer<'_>],
119        server_name: &ServerName,
120        ocsp_response: &[u8],
121        now: UnixTime,
122    ) -> std::result::Result<ServerCertVerified, rustls::Error> {
123        self.inner.verify_server_cert(
124            end_entity,
125            intermediates,
126            server_name,
127            ocsp_response,
128            now,
129        )?;
130        if TlsServerConfig::calculate_cert_hash(end_entity) == self.fingerprint {
131            Ok(ServerCertVerified::assertion())
132        } else {
133            Err(rustls::Error::General(
134                "Pinned certificate hash mismatch".into(),
135            ))
136        }
137    }
138
139    fn verify_tls12_signature(
140        &self,
141        message: &[u8],
142        cert: &CertificateDer<'_>,
143        dss: &DigitallySignedStruct,
144    ) -> std::result::Result<HandshakeSignatureValid, rustls::Error> {
145        self.inner.verify_tls12_signature(message, cert, dss)
146    }
147
148    fn verify_tls13_signature(
149        &self,
150        message: &[u8],
151        cert: &CertificateDer<'_>,
152        dss: &DigitallySignedStruct,
153    ) -> std::result::Result<HandshakeSignatureValid, rustls::Error> {
154        self.inner.verify_tls13_signature(message, cert, dss)
155    }
156
157    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
158        self.inner.supported_verify_schemes()
159    }
160}
161
162/// Accepts any server certificate (insecure mode without a pin), but still requires a
163/// valid handshake signature from the key in that certificate.
164#[derive(Debug)]
165struct AcceptAnyServerCert {
166    supported_algs: WebPkiSupportedAlgorithms,
167}
168
169impl ServerCertVerifier for AcceptAnyServerCert {
170    fn verify_server_cert(
171        &self,
172        _end_entity: &CertificateDer<'_>,
173        _intermediates: &[CertificateDer<'_>],
174        _server_name: &ServerName,
175        _ocsp_response: &[u8],
176        _now: UnixTime,
177    ) -> std::result::Result<ServerCertVerified, rustls::Error> {
178        Ok(ServerCertVerified::assertion())
179    }
180
181    fn verify_tls12_signature(
182        &self,
183        message: &[u8],
184        cert: &CertificateDer<'_>,
185        dss: &DigitallySignedStruct,
186    ) -> std::result::Result<HandshakeSignatureValid, rustls::Error> {
187        rustls::crypto::verify_tls12_signature(message, cert, dss, &self.supported_algs)
188    }
189
190    fn verify_tls13_signature(
191        &self,
192        message: &[u8],
193        cert: &CertificateDer<'_>,
194        dss: &DigitallySignedStruct,
195    ) -> std::result::Result<HandshakeSignatureValid, rustls::Error> {
196        rustls::crypto::verify_tls13_signature(message, cert, dss, &self.supported_algs)
197    }
198
199    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
200        self.supported_algs.supported_schemes()
201    }
202}
203
204/// The crypto provider used by every TLS config and verifier in this module.
205///
206/// rustls APIs without a `_with_provider` suffix fall back to the process-level default
207/// provider, which panics when more than one rustls crypto backend is compiled in and
208/// none has been installed. Always pass this provider explicitly instead.
209fn crypto_provider() -> Arc<CryptoProvider> {
210    Arc::new(rustls::crypto::ring::default_provider())
211}
212
213/// Resolves the versions and cipher suites requested with `with_tls_versions()` and
214/// `with_cipher_suites()` into the protocol versions to enable and a provider whose
215/// cipher suites are restricted to the requested ones.
216///
217/// `None` means the rustls defaults. Cipher suites are matched by their IANA identifier
218/// against the suites of [`crypto_provider()`], keeping that provider's preference order.
219/// Requests that leave nothing usable are rejected here rather than falling back to the
220/// defaults.
221fn resolve_protocol_settings(
222    tls_versions: Option<&[TlsVersion]>,
223    cipher_suites: Option<&[rustls::SupportedCipherSuite]>,
224) -> Result<(
225    Arc<CryptoProvider>,
226    Vec<&'static rustls::SupportedProtocolVersion>,
227)> {
228    let versions: Vec<&'static rustls::SupportedProtocolVersion> = match tls_versions {
229        None => rustls::DEFAULT_VERSIONS.to_vec(),
230        Some(requested) => {
231            let mut has_tls13 = false;
232            let mut has_tls12 = false;
233            for v in requested {
234                match v {
235                    TlsVersion::TLS12 => has_tls12 = true,
236                    TlsVersion::TLS13 => has_tls13 = true,
237                    TlsVersion::All => {
238                        has_tls13 = true;
239                        has_tls12 = true;
240                    }
241                }
242            }
243            debug!(
244                "TLS versions requested: TLS1.2={}, TLS1.3={}",
245                has_tls12, has_tls13
246            );
247
248            let mut versions = Vec::with_capacity(2);
249            if has_tls13 {
250                versions.push(&rustls::version::TLS13);
251            }
252            if has_tls12 {
253                versions.push(&rustls::version::TLS12);
254            }
255            if versions.is_empty() {
256                return Err(ProtocolError::TlsError(
257                    "No TLS protocol versions configured: with_tls_versions() was given an empty list"
258                        .into(),
259                ));
260            }
261            versions
262        }
263    };
264
265    let mut provider = CryptoProvider::clone(&crypto_provider());
266    if let Some(requested) = cipher_suites {
267        for suite in requested {
268            if !provider
269                .cipher_suites
270                .iter()
271                .any(|s| s.suite() == suite.suite())
272            {
273                warn!(suite = ?suite.suite(), "Requested cipher suite is not supported and was ignored");
274            }
275        }
276        provider
277            .cipher_suites
278            .retain(|s| requested.iter().any(|r| r.suite() == s.suite()));
279        if provider.cipher_suites.is_empty() {
280            return Err(ProtocolError::TlsError(
281                "No supported cipher suites configured: none of the suites given to with_cipher_suites() are available"
282                    .into(),
283            ));
284        }
285        debug!(
286            suites = ?provider.cipher_suites.iter().map(|s| s.suite()).collect::<Vec<_>>(),
287            "TLS cipher suites restricted"
288        );
289    }
290
291    if !provider
292        .cipher_suites
293        .iter()
294        .any(|s| versions.contains(&s.version()))
295    {
296        return Err(ProtocolError::TlsError(
297            "None of the configured cipher suites can be used with the configured TLS versions"
298                .into(),
299        ));
300    }
301
302    Ok((Arc::new(provider), versions))
303}
304
305/// Helper function to load a private key from PKCS8 format
306fn load_private_key(reader: &mut BufReader<File>) -> Result<PrivateKeyDer<'static>> {
307    // Try to load PKCS8 keys
308    // Seek to beginning of file first
309    reader
310        .seek(std::io::SeekFrom::Start(0))
311        .map_err(ProtocolError::Io)?;
312
313    // Collect every PKCS8 private key section in the PEM input
314    let keys: std::result::Result<Vec<_>, _> =
315        PrivatePkcs8KeyDer::pem_reader_iter(reader).collect();
316    let keys =
317        keys.map_err(|_| ProtocolError::TlsError("Failed to parse PKCS8 private key".into()))?;
318
319    if !keys.is_empty() {
320        return Ok(PrivateKeyDer::Pkcs8(keys[0].clone_key()));
321    }
322
323    // Note: Add support for other key formats like RSA or EC if needed
324
325    Err(ProtocolError::TlsError(
326        "No supported private key found: the key file must hold a PEM \"PRIVATE KEY\" (PKCS#8) block"
327            .into(),
328    ))
329}
330
331/// TLS protocol version
332pub enum TlsVersion {
333    /// TLS 1.2
334    TLS12,
335    /// TLS 1.3
336    TLS13,
337    /// Both TLS 1.2 and 1.3
338    All,
339}
340
341/// TLS server configuration
342pub struct TlsServerConfig {
343    cert_path: String,
344    key_path: String,
345    /// Optional path to client CA certificates for mTLS
346    client_ca_path: Option<String>,
347    /// Whether to require client certificates (mTLS)
348    require_client_auth: bool,
349    /// Allowed TLS protocol versions (None = use rustls defaults)
350    tls_versions: Option<Vec<TlsVersion>>,
351    /// Allowed cipher suites (None = use rustls defaults)
352    cipher_suites: Option<Vec<rustls::SupportedCipherSuite>>,
353    /// ALPN protocols to advertise (default: ["h2", "http/1.1"])
354    alpn_protocols: Option<Vec<Vec<u8>>>,
355}
356
357impl TlsServerConfig {
358    /// Create a new TLS server configuration
359    pub fn new<P: AsRef<std::path::Path>>(cert_path: P, key_path: P) -> Self {
360        Self {
361            cert_path: cert_path.as_ref().to_string_lossy().to_string(),
362            key_path: key_path.as_ref().to_string_lossy().to_string(),
363            client_ca_path: None,
364            require_client_auth: false,
365            tls_versions: None,
366            cipher_suites: None,
367            alpn_protocols: Some(vec![b"h2".to_vec(), b"http/1.1".to_vec()]),
368        }
369    }
370
371    /// Set allowed TLS protocol versions
372    ///
373    /// Only the listed versions are enabled. An empty list is rejected when the
374    /// config is loaded.
375    pub fn with_tls_versions(mut self, versions: Vec<TlsVersion>) -> Self {
376        self.tls_versions = Some(versions);
377        self
378    }
379
380    /// Set allowed cipher suites
381    ///
382    /// Only the listed suites are enabled, matched by suite identifier and kept in this
383    /// crate's preference order. Loading the config fails if none of them is supported,
384    /// or if none of them can be used with the enabled TLS versions.
385    pub fn with_cipher_suites(mut self, cipher_suites: Vec<rustls::SupportedCipherSuite>) -> Self {
386        self.cipher_suites = Some(cipher_suites);
387        self
388    }
389
390    /// Enable mutual TLS authentication by providing a CA certificate path
391    pub fn with_client_auth<S: Into<String>>(mut self, client_ca_path: S) -> Self {
392        self.client_ca_path = Some(client_ca_path.into());
393        self.require_client_auth = true;
394        self
395    }
396
397    /// Set whether client authentication is required (true) or optional (false)
398    ///
399    /// Client certificates are verified against the CA given to `with_client_auth()`,
400    /// which also sets this to `true`. Call it after `with_client_auth()` to make client
401    /// authentication optional: clients without a certificate are then accepted, but a
402    /// certificate that is presented must still be signed by the client CA.
403    ///
404    /// `require_client_auth(true)` without `with_client_auth()` has no CA to verify
405    /// against, so `load_server_config()` returns an error instead of silently accepting
406    /// clients without a certificate.
407    pub fn require_client_auth(mut self, required: bool) -> Self {
408        self.require_client_auth = required;
409        self
410    }
411
412    /// Set ALPN protocols to advertise during TLS handshake
413    pub fn with_alpn_protocols(mut self, protocols: Vec<Vec<u8>>) -> Self {
414        self.alpn_protocols = Some(protocols);
415        self
416    }
417
418    /// Generate a self-signed certificate for development/testing purposes
419    pub fn generate_self_signed<P: AsRef<Path>>(cert_path: P, key_path: P) -> io::Result<Self> {
420        let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()])
421            .map_err(|e| io::Error::other(format!("Certificate generation error: {e}")))?;
422
423        // Write certificate
424        let mut cert_file = File::create(&cert_path)?;
425        let pem = cert.cert.pem();
426        cert_file.write_all(pem.as_bytes())?;
427
428        // Write private key, readable by the owner only on Unix
429        let mut key_file = create_private_key_file(key_path.as_ref())?;
430        key_file.write_all(cert.signing_key.serialize_pem().as_bytes())?;
431
432        Ok(Self {
433            cert_path: cert_path.as_ref().to_string_lossy().to_string(),
434            key_path: key_path.as_ref().to_string_lossy().to_string(),
435            client_ca_path: None,
436            require_client_auth: false,
437            tls_versions: None,
438            cipher_suites: None,
439            alpn_protocols: Some(vec![b"h2".to_vec(), b"http/1.1".to_vec()]),
440        })
441    }
442
443    /// Load the TLS configuration from files
444    ///
445    /// # Errors
446    /// Returns `ProtocolError::TlsError` if a file cannot be read or parsed, if the
447    /// version or cipher-suite settings leave nothing usable, or if client
448    /// authentication is required without a client CA (see `require_client_auth()`).
449    pub fn load_server_config(&self) -> Result<ServerConfig> {
450        // Required client authentication needs a CA to verify client certificates
451        // against. Without one, rustls would be built with no client auth at all and
452        // every client would be accepted, so refuse the configuration instead.
453        if self.require_client_auth && self.client_ca_path.is_none() {
454            return Err(ProtocolError::TlsError(
455                "client authentication is required but no client CA is configured: call \
456                 with_client_auth(\"<client-ca.pem>\") with the CA that signs client \
457                 certificates, or drop require_client_auth(true)"
458                    .into(),
459            ));
460        }
461
462        // Load certificate
463        let cert_file = File::open(&self.cert_path)
464            .map_err(|e| ProtocolError::TlsError(format!("Failed to open cert file: {e}")))?;
465        let mut cert_reader = BufReader::new(cert_file);
466        let cert_chain: std::result::Result<Vec<_>, _> =
467            CertificateDer::pem_reader_iter(&mut cert_reader).collect();
468        let cert_chain: Vec<CertificateDer<'static>> = cert_chain
469            .map_err(|_| ProtocolError::TlsError("Failed to parse certificate".into()))?;
470
471        if cert_chain.is_empty() {
472            return Err(ProtocolError::TlsError("No certificates found".into()));
473        }
474
475        // Load private key
476        let key_file = File::open(&self.key_path)
477            .map_err(|e| ProtocolError::TlsError(format!("Failed to open key file: {e}")))?;
478        let mut key_reader = BufReader::new(key_file);
479        let private_key = load_private_key(&mut key_reader)?;
480
481        let (provider, versions) =
482            resolve_protocol_settings(self.tls_versions.as_deref(), self.cipher_suites.as_deref())?;
483        let config_builder = ServerConfig::builder_with_provider(provider.clone())
484            .with_protocol_versions(&versions)
485            .map_err(|e| {
486                ProtocolError::TlsError(format!("Failed to configure TLS protocol versions: {e}"))
487            })?;
488
489        // Configure client authentication (mTLS) if a client CA was given
490        let cert_builder = if let Some(client_ca_path) = &self.client_ca_path {
491            // Load client CA certificates
492            let client_ca_file = File::open(client_ca_path).map_err(|e| {
493                ProtocolError::TlsError(format!("Failed to open client CA file: {e}"))
494            })?;
495            let mut client_ca_reader = BufReader::new(client_ca_file);
496            let client_ca_certs: std::result::Result<Vec<_>, _> =
497                CertificateDer::pem_reader_iter(&mut client_ca_reader).collect();
498            let client_ca_certs: Vec<CertificateDer<'static>> = client_ca_certs.map_err(|_| {
499                ProtocolError::TlsError("Failed to parse client CA certificate".into())
500            })?;
501
502            if client_ca_certs.is_empty() {
503                return Err(ProtocolError::TlsError(
504                    "No client CA certificates found".into(),
505                ));
506            }
507
508            // Create client cert verifier
509            let mut client_root_store = RootCertStore::empty();
510            for cert in client_ca_certs {
511                client_root_store.add(cert).map_err(|e| {
512                    ProtocolError::TlsError(format!("Failed to add client CA cert: {e}"))
513                })?;
514            }
515
516            // Create client authentication verifier using WebPkiClientVerifier.
517            // The provider is passed explicitly: `WebPkiClientVerifier::builder` uses the
518            // process-level default, which panics when it cannot be chosen automatically.
519            let mut verifier_builder = rustls::server::WebPkiClientVerifier::builder_with_provider(
520                Arc::new(client_root_store),
521                provider,
522            );
523            if !self.require_client_auth {
524                // Optional client auth: a client without a certificate is accepted, but a
525                // certificate that is presented must still chain to the client CA.
526                verifier_builder = verifier_builder.allow_unauthenticated();
527            }
528            let client_auth = verifier_builder.build().map_err(|e| {
529                ProtocolError::TlsError(format!("Failed to build client verifier: {e}"))
530            })?;
531
532            if self.require_client_auth {
533                debug!("mTLS enabled with client certificate verification required");
534            } else {
535                debug!("mTLS enabled with optional client certificate verification");
536            }
537            config_builder.with_client_cert_verifier(client_auth)
538        } else {
539            config_builder.with_no_client_auth()
540        };
541
542        // Build config with certificates
543        let mut config = cert_builder
544            .with_single_cert(cert_chain, private_key)
545            .map_err(|e| ProtocolError::TlsError(format!("TLS error: {e}")))?;
546
547        // Configure ALPN protocols if specified
548        if let Some(protocols) = &self.alpn_protocols {
549            config.alpn_protocols = protocols.clone();
550            debug!(
551                protocol_count = protocols.len(),
552                "ALPN protocols configured"
553            );
554        }
555
556        Ok(config)
557    }
558
559    /// Calculate SHA-256 hash for a certificate to use with pinning
560    pub fn calculate_cert_hash(cert: &CertificateDer<'_>) -> Vec<u8> {
561        use sha2::{Digest, Sha256};
562        let mut hasher = Sha256::new();
563        hasher.update(cert.as_ref());
564        hasher.finalize().to_vec()
565    }
566}
567
568/// TLS Client Configuration
569pub struct TlsClientConfig {
570    server_name: String,
571    insecure: bool,
572    /// Optional certificate hash to pin (SHA-256 fingerprint)
573    pinned_cert_hash: Option<Vec<u8>>,
574    /// Optional client certificate path for mTLS
575    client_cert_path: Option<String>,
576    /// Optional client key path for mTLS
577    client_key_path: Option<String>,
578    /// Optional CA certificate file to trust instead of the system roots
579    root_ca_path: Option<String>,
580    /// Allowed TLS protocol versions (None = use rustls defaults)
581    tls_versions: Option<Vec<TlsVersion>>,
582    /// Allowed cipher suites (None = use rustls defaults)
583    cipher_suites: Option<Vec<rustls::SupportedCipherSuite>>,
584}
585
586/// Length of a certificate pin: a SHA-256 digest.
587const PIN_LEN: usize = 32;
588
589impl TlsClientConfig {
590    /// Create a new TLS client configuration
591    pub fn new<S: Into<String>>(server_name: S) -> Self {
592        Self {
593            server_name: server_name.into(),
594            insecure: false,
595            pinned_cert_hash: None,
596            client_cert_path: None,
597            client_key_path: None,
598            root_ca_path: None,
599            tls_versions: None,
600            cipher_suites: None,
601        }
602    }
603
604    /// Set allowed TLS protocol versions
605    ///
606    /// Only the listed versions are enabled. An empty list is rejected when the
607    /// config is loaded.
608    pub fn with_tls_versions(mut self, versions: Vec<TlsVersion>) -> Self {
609        self.tls_versions = Some(versions);
610        self
611    }
612
613    /// Set allowed cipher suites
614    ///
615    /// Only the listed suites are enabled, matched by suite identifier and kept in this
616    /// crate's preference order. Loading the config fails if none of them is supported,
617    /// or if none of them can be used with the enabled TLS versions.
618    pub fn with_cipher_suites(mut self, cipher_suites: Vec<rustls::SupportedCipherSuite>) -> Self {
619        self.cipher_suites = Some(cipher_suites);
620        self
621    }
622
623    /// Trust the CA certificates in `ca_path` (PEM) instead of the system roots
624    ///
625    /// Use this for servers with certificates from a private CA. Only the given CAs are
626    /// trusted; the system root store is not loaded. Hostname and chain validation
627    /// still apply, and a pin set with `with_pinned_cert_hash()` is checked on top.
628    ///
629    /// Cannot be combined with `insecure()`, which skips CA validation:
630    /// `load_client_config()` returns an error for that combination.
631    pub fn with_root_ca<S: Into<String>>(mut self, ca_path: S) -> Self {
632        self.root_ca_path = Some(ca_path.into());
633        self
634    }
635
636    /// Configure client authentication for mTLS
637    pub fn with_client_certificate<S: Into<String>>(mut self, cert_path: S, key_path: S) -> Self {
638        self.client_cert_path = Some(cert_path.into());
639        self.client_key_path = Some(key_path.into());
640        self
641    }
642
643    /// Allow insecure connections (skip certificate verification)
644    ///
645    /// # WARNING: Security Risk
646    /// This mode skips certificate validation (no CA chain or hostname checks), so any
647    /// certificate is accepted unless one is pinned with `with_pinned_cert_hash()`. The
648    /// server must still prove that it holds the private key for the certificate it
649    /// presents: the handshake signature is always verified. This mode should ONLY be used for:
650    /// - Development and testing
651    /// - Debugging environments
652    /// - Internal networks with certificate pinning enabled
653    ///
654    /// **NEVER** use this in production without explicit certificate pinning via `with_pinned_cert_hash()`.
655    /// For a server with a private CA, trust that CA with `with_root_ca()` instead.
656    pub fn insecure(mut self) -> Self {
657        warn!("INSECURE MODE ENABLED: Certificate verification is disabled. This should only be used for development/testing.");
658        self.insecure = true;
659        self
660    }
661
662    /// Pin a certificate by its SHA-256 hash/fingerprint
663    ///
664    /// Only a server presenting the certificate with exactly this hash is accepted.
665    /// Compute the hash with `TlsServerConfig::calculate_cert_hash()`: it is the 32-byte
666    /// SHA-256 digest of the DER certificate, as raw bytes (not hex).
667    ///
668    /// Without `insecure()` the pin is checked in addition to normal CA and hostname
669    /// validation. With `insecure()` it replaces them, for development setups with a
670    /// self-signed certificate. Either way the handshake signature is verified against
671    /// the pinned certificate's public key, so a server that has the certificate but not
672    /// its private key is rejected.
673    ///
674    /// A hash that is not 32 bytes long can never match, so `load_client_config()`
675    /// returns an error for it.
676    pub fn with_pinned_cert_hash(mut self, hash: Vec<u8>) -> Self {
677        self.pinned_cert_hash = Some(hash);
678        self
679    }
680
681    /// Load the TLS client configuration
682    ///
683    /// # Errors
684    /// Returns `ProtocolError::TlsError` if a pinned hash is not 32 bytes, if
685    /// `with_root_ca()` is combined with `insecure()`, if a certificate or key file
686    /// cannot be read or parsed, or if the version or cipher-suite settings leave
687    /// nothing usable.
688    pub fn load_client_config(&self) -> Result<ClientConfig> {
689        if let Some(hash) = &self.pinned_cert_hash {
690            if hash.len() != PIN_LEN {
691                return Err(ProtocolError::TlsError(format!(
692                    "pinned certificate hash must be {PIN_LEN} bytes (the SHA-256 digest of the \
693                     DER certificate), got {} bytes: compute it with \
694                     TlsServerConfig::calculate_cert_hash(&cert)",
695                    hash.len()
696                )));
697            }
698        }
699        if self.insecure && self.root_ca_path.is_some() {
700            return Err(ProtocolError::TlsError(
701                "with_root_ca() has no effect with insecure(), which skips CA validation: \
702                 remove insecure() to validate against the CA, or remove with_root_ca()"
703                    .into(),
704            ));
705        }
706        if self.insecure {
707            self.build_insecure_client_config()
708        } else {
709            self.build_secure_client_config()
710        }
711    }
712
713    /// Start a client config builder with the configured provider, versions and suites
714    fn client_config_builder(
715        &self,
716    ) -> Result<(
717        Arc<CryptoProvider>,
718        rustls::ConfigBuilder<ClientConfig, rustls::WantsVerifier>,
719    )> {
720        let (provider, versions) =
721            resolve_protocol_settings(self.tls_versions.as_deref(), self.cipher_suites.as_deref())?;
722        let builder = ClientConfig::builder_with_provider(provider.clone())
723            .with_protocol_versions(&versions)
724            .map_err(|e| {
725                ProtocolError::TlsError(format!("Failed to configure TLS protocol versions: {e}"))
726            })?;
727        Ok((provider, builder))
728    }
729
730    /// Build secure client config with system root CAs (or the `with_root_ca()` CAs)
731    fn build_secure_client_config(&self) -> Result<ClientConfig> {
732        let (provider, builder) = self.client_config_builder()?;
733        let root_store = match &self.root_ca_path {
734            Some(path) => load_root_ca(path)?,
735            None => self.load_system_root_certificates()?,
736        };
737        let builder = match &self.pinned_cert_hash {
738            // A pin narrows CA validation: webpki checks the chain and hostname, then the
739            // fingerprint must match too.
740            Some(hash) => {
741                let inner =
742                    WebPkiServerVerifier::builder_with_provider(Arc::new(root_store), provider)
743                        .build()
744                        .map_err(|e| {
745                            ProtocolError::TlsError(format!(
746                                "Failed to build server certificate verifier: {e}"
747                            ))
748                        })?;
749                builder
750                    .dangerous()
751                    .with_custom_certificate_verifier(Arc::new(PinnedWebPkiVerifier {
752                        inner,
753                        fingerprint: hash.clone(),
754                    }))
755            }
756            None => builder.with_root_certificates(root_store),
757        };
758
759        // Apply client auth directly
760        if let (Some(client_cert_path), Some(client_key_path)) =
761            (&self.client_cert_path, &self.client_key_path)
762        {
763            let (cert_chain, key) =
764                self.load_client_credentials(client_cert_path, client_key_path)?;
765            builder.with_client_auth_cert(cert_chain, key).map_err(|e| {
766                ProtocolError::TlsError(format!("Failed to set client certificate: {e}"))
767            })
768        } else {
769            Ok(builder.with_no_client_auth())
770        }
771    }
772
773    /// Build insecure client config with custom verifier
774    fn build_insecure_client_config(&self) -> Result<ClientConfig> {
775        let (provider, builder) = self.client_config_builder()?;
776        let verifier = self.create_custom_verifier(&provider);
777        let custom_builder = builder
778            .dangerous()
779            .with_custom_certificate_verifier(verifier);
780
781        // Apply client auth directly
782        if let (Some(client_cert_path), Some(client_key_path)) =
783            (&self.client_cert_path, &self.client_key_path)
784        {
785            let (cert_chain, key) =
786                self.load_client_credentials(client_cert_path, client_key_path)?;
787            custom_builder
788                .with_client_auth_cert(cert_chain, key)
789                .map_err(|e| {
790                    ProtocolError::TlsError(format!("Failed to set client certificate: {e}"))
791                })
792        } else {
793            Ok(custom_builder.with_no_client_auth())
794        }
795    }
796
797    /// Load system root certificates
798    fn load_system_root_certificates(&self) -> Result<RootCertStore> {
799        let mut root_store = RootCertStore::empty();
800        let native_certs = rustls_native_certs::load_native_certs();
801        if let Some(e) = native_certs.errors.first() {
802            return Err(ProtocolError::TlsError(format!(
803                "Failed to load native certs: {e}"
804            )));
805        }
806
807        for cert in native_certs.certs {
808            root_store.add(cert).map_err(|e| {
809                ProtocolError::TlsError(format!("Failed to add cert to root store: {e}"))
810            })?;
811        }
812
813        if root_store.is_empty() {
814            return Err(ProtocolError::TlsError(
815                "No trusted root certificates found in the system store: install the system \
816                 CA bundle, or trust a CA explicitly with with_root_ca(\"<ca.pem>\")"
817                    .into(),
818            ));
819        }
820
821        Ok(root_store)
822    }
823
824    /// Create custom certificate verifier (pinning or accept-any)
825    ///
826    /// Handshake signatures are checked with the same provider's signature algorithms.
827    fn create_custom_verifier(&self, provider: &CryptoProvider) -> Arc<dyn ServerCertVerifier> {
828        let supported_algs = provider.signature_verification_algorithms;
829        if let Some(hash) = &self.pinned_cert_hash {
830            Arc::new(CertificateFingerprint {
831                fingerprint: hash.clone(),
832                supported_algs,
833            })
834        } else {
835            Arc::new(AcceptAnyServerCert { supported_algs })
836        }
837    }
838
839    /// Load client certificate and private key
840    fn load_client_credentials(
841        &self,
842        cert_path: &str,
843        key_path: &str,
844    ) -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)> {
845        // Load certificate
846        let cert_file = File::open(cert_path).map_err(ProtocolError::Io)?;
847        let mut cert_reader = BufReader::new(cert_file);
848        let certs_result: std::result::Result<Vec<_>, _> =
849            CertificateDer::pem_reader_iter(&mut cert_reader).collect();
850        let certs: Vec<CertificateDer<'static>> = certs_result
851            .map_err(|_| ProtocolError::TlsError("Failed to parse client certificate".into()))?;
852
853        if certs.is_empty() {
854            return Err(ProtocolError::TlsError(
855                "No client certificates found".into(),
856            ));
857        }
858
859        // Load private key
860        let key_file = File::open(key_path).map_err(ProtocolError::Io)?;
861        let mut key_reader = BufReader::new(key_file);
862        let key = load_private_key(&mut key_reader)?;
863
864        Ok((certs, key))
865    }
866
867    /// Get the server name as a rustls::ServerName
868    pub fn server_name(&self) -> Result<ServerName<'_>> {
869        ServerName::try_from(self.server_name.as_str())
870            .map_err(|_| ProtocolError::TlsError("Invalid server name".into()))
871    }
872
873    /// Get the server name as an owned String
874    pub fn server_name_string(&self) -> String {
875        self.server_name.clone()
876    }
877
878    /// Everything that determines the rustls client config this builds, as a string.
879    /// Two configs with the same key build equivalent rustls configs (as long as the
880    /// files they name are unchanged), so `SessionCache` can reuse one.
881    pub(crate) fn resumption_key(&self) -> String {
882        let versions = self.tls_versions.as_ref().map(|versions| {
883            versions
884                .iter()
885                .map(|v| match v {
886                    TlsVersion::TLS12 => "1.2",
887                    TlsVersion::TLS13 => "1.3",
888                    TlsVersion::All => "all",
889                })
890                .collect::<Vec<_>>()
891        });
892        let suites = self
893            .cipher_suites
894            .as_ref()
895            .map(|suites| suites.iter().map(|s| s.suite()).collect::<Vec<_>>());
896        format!(
897            "{:?}|{}|{:?}|{:?}|{:?}|{:?}|{:?}|{:?}",
898            self.server_name,
899            self.insecure,
900            self.pinned_cert_hash,
901            self.client_cert_path,
902            self.client_key_path,
903            self.root_ca_path,
904            versions,
905            suites
906        )
907    }
908
909    /// The server name as an owned `ServerName<'static>`, as `TlsConnector::connect`
910    /// needs.
911    pub(crate) fn owned_server_name(&self) -> Result<ServerName<'static>> {
912        ServerName::try_from(self.server_name.clone())
913            .map_err(|_| ProtocolError::TlsError("Invalid server name".into()))
914    }
915}
916
917/// Load the CA certificates in the PEM file at `path` into a root store.
918fn load_root_ca(path: &str) -> Result<RootCertStore> {
919    let file = File::open(path)
920        .map_err(|e| ProtocolError::TlsError(format!("Failed to open root CA file: {e}")))?;
921    let mut reader = BufReader::new(file);
922    let certs: std::result::Result<Vec<_>, _> =
923        CertificateDer::pem_reader_iter(&mut reader).collect();
924    let certs =
925        certs.map_err(|_| ProtocolError::TlsError("Failed to parse root CA certificate".into()))?;
926    if certs.is_empty() {
927        return Err(ProtocolError::TlsError(
928            "No root CA certificates found".into(),
929        ));
930    }
931    let mut store = RootCertStore::empty();
932    for cert in certs {
933        store
934            .add(cert)
935            .map_err(|e| ProtocolError::TlsError(format!("Failed to add root CA cert: {e}")))?;
936    }
937    Ok(store)
938}
939
940/// Create a file for a private key. On Unix it is created with mode 0600 so the key
941/// is not readable by other users; elsewhere the platform default applies.
942fn create_private_key_file(path: &Path) -> io::Result<File> {
943    let mut options = std::fs::OpenOptions::new();
944    options.write(true).create(true).truncate(true);
945    #[cfg(unix)]
946    {
947        use std::os::unix::fs::OpenOptionsExt;
948        options.mode(0o600);
949    }
950    options.open(path)
951}
952
953/// Start a TLS server on the given address
954#[instrument(skip(config))]
955pub async fn start_server(addr: &str, config: TlsServerConfig) -> Result<()> {
956    let tls_config = config.load_server_config()?;
957    let acceptor = TlsAcceptor::from(Arc::new(tls_config));
958    let listener = TcpListener::bind(addr).await?;
959
960    info!(address=%addr, "TLS server listening");
961
962    loop {
963        let (stream, peer) = listener.accept().await?;
964        let acceptor = acceptor.clone();
965
966        tokio::spawn(async move {
967            // A client that never finishes the handshake must not hold the connection
968            // open indefinitely.
969            match tokio::time::timeout(HANDSHAKE_TIMEOUT, acceptor.accept(stream)).await {
970                Ok(Ok(tls_stream)) => {
971                    if let Err(e) = handle_tls_connection(tls_stream, peer).await {
972                        error!(%peer, error=%e, "Connection error");
973                    }
974                }
975                Ok(Err(e)) => {
976                    error!(%peer, error=%e, "TLS handshake failed");
977                }
978                Err(_) => {
979                    warn!(%peer, "TLS handshake timed out");
980                }
981            }
982        });
983    }
984}
985
986/// Handle a TLS connection
987#[instrument(skip(tls_stream), fields(peer=%peer))]
988async fn handle_tls_connection(
989    tls_stream: ServerTlsStream<TcpStream>,
990    peer: SocketAddr,
991) -> Result<()> {
992    let mut framed = Framed::new(tls_stream, PacketCodec);
993
994    info!("TLS connection established");
995
996    while let Some(packet) = framed.next().await {
997        match packet {
998            Ok(pkt) => {
999                debug!(bytes = pkt.payload.len(), "Received data");
1000                on_packet(pkt, &mut framed).await?;
1001            }
1002            Err(e) => {
1003                error!(error=%e, "Protocol error");
1004                break;
1005            }
1006        }
1007    }
1008
1009    info!("TLS connection closed");
1010    Ok(())
1011}
1012
1013/// Handle incoming TLS packets
1014#[instrument(skip(framed), fields(packet_version=pkt.version, payload_size=pkt.payload.len()))]
1015async fn on_packet<T>(pkt: Packet, framed: &mut Framed<T, PacketCodec>) -> Result<()>
1016where
1017    T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin,
1018{
1019    // Echo the packet back (sample implementation)
1020    let response = Packet {
1021        version: pkt.version,
1022        payload: pkt.payload,
1023    };
1024
1025    framed.send(response).await?;
1026    Ok(())
1027}
1028
1029/// Connect to a TLS server
1030pub async fn connect(
1031    addr: &str,
1032    config: TlsClientConfig,
1033) -> Result<Framed<ClientTlsStream<TcpStream>, PacketCodec>> {
1034    let tls_config = Arc::new(config.load_client_config()?);
1035    let connector = TlsConnector::from(tls_config);
1036
1037    let domain = config.owned_server_name()?;
1038
1039    let stream = TcpStream::connect(addr).await?;
1040
1041    let tls_stream = tokio::time::timeout(HANDSHAKE_TIMEOUT, connector.connect(domain, stream))
1042        .await
1043        .map_err(|_| ProtocolError::Timeout)?
1044        .map_err(|e| ProtocolError::TlsError(format!("TLS connection failed: {e}")))?;
1045
1046    let framed = Framed::new(tls_stream, PacketCodec);
1047    Ok(framed)
1048}