Skip to main content

remote/
tls.rs

1//! TLS support for encrypted and authenticated connections.
2//!
3//! This module provides certificate generation and TLS configuration for:
4//! - Master↔rcpd connections (rcpd is server; mutual fingerprint verification — master
5//!   verifies rcpd's server cert, rcpd verifies master's client cert)
6//! - Source↔Destination connections (source is server; mutual TLS with client certificates)
7use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
8use rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName, UnixTime};
9use rustls::server::danger::{ClientCertVerified, ClientCertVerifier};
10use rustls::{
11    ClientConfig, DigitallySignedStruct, DistinguishedName, ServerConfig, SignatureScheme,
12};
13use sha2::{Digest, Sha256};
14use std::sync::Arc;
15
16/// A certificate fingerprint (SHA-256 of DER-encoded certificate).
17pub type Fingerprint = [u8; 32];
18
19/// rcp pins TLS 1.3 for every connection — TLS 1.2 is never negotiated. This is safe to do
20/// unconditionally: TLS 1.3 has been available at both ends since the TLS layer was introduced
21/// (rustls has always offered it), so pinning never excludes a legitimate rcp/rcpd peer. A peer
22/// that cannot negotiate 1.3 fails the handshake — the correct outcome — rather than silently
23/// downgrading. (This does not rely on the version check, which auto-deploy can bypass.)
24const TLS_VERSIONS: &[&rustls::SupportedProtocolVersion] = &[&rustls::version::TLS13];
25
26/// A certified key pair (certificate + private key) with its fingerprint.
27#[derive(Clone)]
28pub struct CertifiedKey {
29    pub cert_der: Vec<u8>,
30    pub key_der: Vec<u8>,
31    pub fingerprint: Fingerprint,
32}
33
34/// Generates an ephemeral self-signed certificate using Ed25519.
35///
36/// The certificate is valid for 1 day (doesn't matter since ephemeral).
37/// Returns the certificate, private key, and fingerprint.
38pub fn generate_self_signed_cert() -> anyhow::Result<CertifiedKey> {
39    use rcgen::{CertificateParams, KeyPair};
40    // generate Ed25519 key pair
41    let key_pair = KeyPair::generate_for(&rcgen::PKCS_ED25519)?;
42    // create certificate parameters with random subject
43    let mut params = CertificateParams::default();
44    params.distinguished_name = rcgen::DistinguishedName::new();
45    params.distinguished_name.push(
46        rcgen::DnType::CommonName,
47        format!("rcp-{}", rand::random::<u64>()),
48    );
49    // self-sign the certificate
50    let cert = params.self_signed(&key_pair)?;
51    let cert_der = cert.der().to_vec();
52    let key_der = key_pair.serialize_der();
53    // compute fingerprint
54    let fingerprint = compute_fingerprint(&cert_der);
55    Ok(CertifiedKey {
56        cert_der,
57        key_der,
58        fingerprint,
59    })
60}
61
62/// Returns the ring provider's signature-verification algorithms, used to check the peer's
63/// TLS `CertificateVerify` signature. Fingerprint pinning proves the peer presented the
64/// expected certificate; this proves the peer also holds the matching private key.
65fn signature_verification_algorithms() -> rustls::crypto::WebPkiSupportedAlgorithms {
66    rustls::crypto::ring::default_provider().signature_verification_algorithms
67}
68
69/// Computes SHA-256 fingerprint of a DER-encoded certificate.
70pub fn compute_fingerprint(cert_der: &[u8]) -> Fingerprint {
71    let mut hasher = Sha256::new();
72    hasher.update(cert_der);
73    hasher.finalize().into()
74}
75
76/// Converts a fingerprint to lowercase hex string (64 characters).
77pub fn fingerprint_to_hex(fp: &Fingerprint) -> String {
78    hex::encode(fp)
79}
80
81/// Parses a fingerprint from hex string.
82pub fn fingerprint_from_hex(s: &str) -> anyhow::Result<Fingerprint> {
83    let bytes = hex::decode(s)?;
84    if bytes.len() != 32 {
85        anyhow::bail!(
86            "fingerprint must be 32 bytes (64 hex chars), got {}",
87            bytes.len()
88        );
89    }
90    let mut fp = [0u8; 32];
91    fp.copy_from_slice(&bytes);
92    Ok(fp)
93}
94
95/// Creates a TLS server config without client authentication.
96///
97/// Test-only — every production listener requires a verified client certificate
98/// (see [`create_server_config_with_client_auth`]).
99#[cfg(test)]
100fn create_server_config(cert_key: &CertifiedKey) -> anyhow::Result<Arc<ServerConfig>> {
101    let cert = CertificateDer::from(cert_key.cert_der.clone());
102    let key = PrivateKeyDer::try_from(cert_key.key_der.clone())
103        .map_err(|e| anyhow::anyhow!("invalid private key: {e}"))?;
104    let config = ServerConfig::builder_with_protocol_versions(TLS_VERSIONS)
105        .with_no_client_auth()
106        .with_single_cert(vec![cert], key)?;
107    Ok(Arc::new(config))
108}
109
110/// Creates a TLS server config with client certificate verification.
111///
112/// Used for all production TLS servers: rcpd's master-facing listener (verifies master's
113/// client cert against `--master-cert-fp`) and source's listeners for the destination
114/// (verifies destination's client cert).
115pub fn create_server_config_with_client_auth(
116    cert_key: &CertifiedKey,
117    expected_client_fingerprint: Fingerprint,
118) -> anyhow::Result<Arc<ServerConfig>> {
119    let cert = CertificateDer::from(cert_key.cert_der.clone());
120    let key = PrivateKeyDer::try_from(cert_key.key_der.clone())
121        .map_err(|e| anyhow::anyhow!("invalid private key: {e}"))?;
122    let client_verifier = Arc::new(FingerprintClientCertVerifier::new(
123        expected_client_fingerprint,
124    ));
125    let config = ServerConfig::builder_with_protocol_versions(TLS_VERSIONS)
126        .with_client_cert_verifier(client_verifier)
127        .with_single_cert(vec![cert], key)?;
128    Ok(Arc::new(config))
129}
130
131/// Creates a TLS client config that verifies the server's certificate fingerprint
132/// without presenting a client certificate.
133///
134/// Test-only — production clients always present a client certificate
135/// (see [`create_client_config_with_cert`]).
136#[cfg(test)]
137fn create_client_config(expected_server_fingerprint: Fingerprint) -> Arc<ClientConfig> {
138    let verifier = Arc::new(FingerprintServerCertVerifier::new(
139        expected_server_fingerprint,
140    ));
141    let config = ClientConfig::builder_with_protocol_versions(TLS_VERSIONS)
142        .dangerous()
143        .with_custom_certificate_verifier(verifier)
144        .with_no_client_auth();
145    Arc::new(config)
146}
147
148/// Creates a TLS client config with a client certificate.
149///
150/// Used for all production TLS clients: master→rcpd connections (master presents its
151/// certificate for rcpd to verify) and destination→source connections (destination
152/// presents its certificate for source to verify).
153pub fn create_client_config_with_cert(
154    client_cert_key: &CertifiedKey,
155    expected_server_fingerprint: Fingerprint,
156) -> anyhow::Result<Arc<ClientConfig>> {
157    let verifier = Arc::new(FingerprintServerCertVerifier::new(
158        expected_server_fingerprint,
159    ));
160    let cert = CertificateDer::from(client_cert_key.cert_der.clone());
161    let key = PrivateKeyDer::try_from(client_cert_key.key_der.clone())
162        .map_err(|e| anyhow::anyhow!("invalid private key: {e}"))?;
163    let config = ClientConfig::builder_with_protocol_versions(TLS_VERSIONS)
164        .dangerous()
165        .with_custom_certificate_verifier(verifier)
166        .with_client_auth_cert(vec![cert], key)?;
167    Ok(Arc::new(config))
168}
169
170/// Server certificate verifier that checks the certificate's fingerprint.
171#[derive(Debug)]
172struct FingerprintServerCertVerifier {
173    expected_fingerprint: Fingerprint,
174}
175
176impl FingerprintServerCertVerifier {
177    fn new(expected_fingerprint: Fingerprint) -> Self {
178        Self {
179            expected_fingerprint,
180        }
181    }
182}
183
184impl ServerCertVerifier for FingerprintServerCertVerifier {
185    fn verify_server_cert(
186        &self,
187        end_entity: &CertificateDer<'_>,
188        _intermediates: &[CertificateDer<'_>],
189        _server_name: &ServerName<'_>,
190        _ocsp_response: &[u8],
191        _now: UnixTime,
192    ) -> Result<ServerCertVerified, rustls::Error> {
193        let actual_fingerprint = compute_fingerprint(end_entity.as_ref());
194        if actual_fingerprint == self.expected_fingerprint {
195            Ok(ServerCertVerified::assertion())
196        } else {
197            tracing::error!(
198                "TLS server certificate fingerprint mismatch: expected {}, got {}",
199                fingerprint_to_hex(&self.expected_fingerprint),
200                fingerprint_to_hex(&actual_fingerprint)
201            );
202            Err(rustls::Error::InvalidCertificate(
203                rustls::CertificateError::BadSignature,
204            ))
205        }
206    }
207    fn verify_tls12_signature(
208        &self,
209        message: &[u8],
210        cert: &CertificateDer<'_>,
211        dss: &DigitallySignedStruct,
212    ) -> Result<HandshakeSignatureValid, rustls::Error> {
213        // the fingerprint check above pins WHICH certificate we accept; this verifies the peer
214        // signed the handshake with that certificate's private key (proof of possession)
215        rustls::crypto::verify_tls12_signature(
216            message,
217            cert,
218            dss,
219            &signature_verification_algorithms(),
220        )
221    }
222    fn verify_tls13_signature(
223        &self,
224        message: &[u8],
225        cert: &CertificateDer<'_>,
226        dss: &DigitallySignedStruct,
227    ) -> Result<HandshakeSignatureValid, rustls::Error> {
228        rustls::crypto::verify_tls13_signature(
229            message,
230            cert,
231            dss,
232            &signature_verification_algorithms(),
233        )
234    }
235    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
236        signature_verification_algorithms().supported_schemes()
237    }
238}
239
240/// Client certificate verifier that checks the certificate's fingerprint.
241#[derive(Debug)]
242struct FingerprintClientCertVerifier {
243    expected_fingerprint: Fingerprint,
244}
245
246impl FingerprintClientCertVerifier {
247    fn new(expected_fingerprint: Fingerprint) -> Self {
248        Self {
249            expected_fingerprint,
250        }
251    }
252}
253
254impl ClientCertVerifier for FingerprintClientCertVerifier {
255    fn root_hint_subjects(&self) -> &[DistinguishedName] {
256        &[]
257    }
258    fn verify_client_cert(
259        &self,
260        end_entity: &CertificateDer<'_>,
261        _intermediates: &[CertificateDer<'_>],
262        _now: UnixTime,
263    ) -> Result<ClientCertVerified, rustls::Error> {
264        let actual_fingerprint = compute_fingerprint(end_entity.as_ref());
265        if actual_fingerprint == self.expected_fingerprint {
266            Ok(ClientCertVerified::assertion())
267        } else {
268            tracing::error!(
269                "TLS client certificate fingerprint mismatch: expected {}, got {}",
270                fingerprint_to_hex(&self.expected_fingerprint),
271                fingerprint_to_hex(&actual_fingerprint)
272            );
273            Err(rustls::Error::InvalidCertificate(
274                rustls::CertificateError::BadSignature,
275            ))
276        }
277    }
278    fn verify_tls12_signature(
279        &self,
280        message: &[u8],
281        cert: &CertificateDer<'_>,
282        dss: &DigitallySignedStruct,
283    ) -> Result<HandshakeSignatureValid, rustls::Error> {
284        // the fingerprint check above pins WHICH certificate we accept; this verifies the peer
285        // signed the handshake with that certificate's private key (proof of possession)
286        rustls::crypto::verify_tls12_signature(
287            message,
288            cert,
289            dss,
290            &signature_verification_algorithms(),
291        )
292    }
293    fn verify_tls13_signature(
294        &self,
295        message: &[u8],
296        cert: &CertificateDer<'_>,
297        dss: &DigitallySignedStruct,
298    ) -> Result<HandshakeSignatureValid, rustls::Error> {
299        rustls::crypto::verify_tls13_signature(
300            message,
301            cert,
302            dss,
303            &signature_verification_algorithms(),
304        )
305    }
306    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
307        signature_verification_algorithms().supported_schemes()
308    }
309    fn client_auth_mandatory(&self) -> bool {
310        true
311    }
312}
313
314#[cfg(test)]
315mod tests {
316    use super::*;
317
318    fn install_crypto_provider() {
319        rustls::crypto::ring::default_provider()
320            .install_default()
321            .ok(); // ignore if already installed
322    }
323
324    #[test]
325    fn test_generate_cert_and_fingerprint() {
326        install_crypto_provider();
327        let cert_key = generate_self_signed_cert().unwrap();
328        assert_eq!(cert_key.fingerprint.len(), 32);
329        assert!(!cert_key.cert_der.is_empty());
330        assert!(!cert_key.key_der.is_empty());
331        // fingerprint should be deterministic
332        let fp2 = compute_fingerprint(&cert_key.cert_der);
333        assert_eq!(cert_key.fingerprint, fp2);
334    }
335
336    #[test]
337    fn test_fingerprint_hex_roundtrip() {
338        install_crypto_provider();
339        let cert_key = generate_self_signed_cert().unwrap();
340        let hex = fingerprint_to_hex(&cert_key.fingerprint);
341        assert_eq!(hex.len(), 64);
342        let fp2 = fingerprint_from_hex(&hex).unwrap();
343        assert_eq!(cert_key.fingerprint, fp2);
344    }
345
346    #[test]
347    fn test_fingerprint_from_hex_invalid() {
348        // wrong length
349        assert!(fingerprint_from_hex("abcd").is_err());
350        // invalid hex
351        assert!(fingerprint_from_hex("zzzz").is_err());
352    }
353
354    #[test]
355    fn test_create_server_config() {
356        install_crypto_provider();
357        let cert_key = generate_self_signed_cert().unwrap();
358        let config = create_server_config(&cert_key).unwrap();
359        assert!(config.alpn_protocols.is_empty());
360    }
361
362    #[test]
363    fn test_create_client_config() {
364        install_crypto_provider();
365        let fp = [0u8; 32];
366        let config = create_client_config(fp);
367        assert!(config.alpn_protocols.is_empty());
368    }
369
370    #[test]
371    fn test_server_fingerprint_verifier_accepts_matching() {
372        install_crypto_provider();
373        let cert_key = generate_self_signed_cert().unwrap();
374        let verifier = FingerprintServerCertVerifier::new(cert_key.fingerprint);
375        let cert = CertificateDer::from(cert_key.cert_der);
376        let server_name = ServerName::try_from("rcp").unwrap();
377        let result = verifier.verify_server_cert(&cert, &[], &server_name, &[], UnixTime::now());
378        assert!(result.is_ok());
379    }
380
381    #[test]
382    fn test_server_fingerprint_verifier_rejects_mismatch() {
383        install_crypto_provider();
384        let cert_key = generate_self_signed_cert().unwrap();
385        // use a different fingerprint (all zeros)
386        let wrong_fingerprint = [0u8; 32];
387        let verifier = FingerprintServerCertVerifier::new(wrong_fingerprint);
388        let cert = CertificateDer::from(cert_key.cert_der);
389        let server_name = ServerName::try_from("rcp").unwrap();
390        let result = verifier.verify_server_cert(&cert, &[], &server_name, &[], UnixTime::now());
391        assert!(result.is_err());
392        // verify it's the right error type
393        match result {
394            Err(rustls::Error::InvalidCertificate(rustls::CertificateError::BadSignature)) => {}
395            other => panic!("expected BadSignature error, got: {:?}", other),
396        }
397    }
398
399    #[test]
400    fn test_client_fingerprint_verifier_accepts_matching() {
401        install_crypto_provider();
402        let cert_key = generate_self_signed_cert().unwrap();
403        let verifier = FingerprintClientCertVerifier::new(cert_key.fingerprint);
404        let cert = CertificateDer::from(cert_key.cert_der);
405        let result = verifier.verify_client_cert(&cert, &[], UnixTime::now());
406        assert!(result.is_ok());
407    }
408
409    #[test]
410    fn test_client_fingerprint_verifier_rejects_mismatch() {
411        install_crypto_provider();
412        let cert_key = generate_self_signed_cert().unwrap();
413        // use a different fingerprint (all zeros)
414        let wrong_fingerprint = [0u8; 32];
415        let verifier = FingerprintClientCertVerifier::new(wrong_fingerprint);
416        let cert = CertificateDer::from(cert_key.cert_der);
417        let result = verifier.verify_client_cert(&cert, &[], UnixTime::now());
418        assert!(result.is_err());
419        // verify it's the right error type
420        match result {
421            Err(rustls::Error::InvalidCertificate(rustls::CertificateError::BadSignature)) => {}
422            other => panic!("expected BadSignature error, got: {:?}", other),
423        }
424    }
425
426    #[test]
427    fn test_client_verifier_requires_auth() {
428        install_crypto_provider();
429        let verifier = FingerprintClientCertVerifier::new([0u8; 32]);
430        assert!(verifier.client_auth_mandatory());
431    }
432}
433
434#[cfg(test)]
435mod integration_tests {
436    use super::*;
437    use tokio::io::{AsyncReadExt, AsyncWriteExt};
438    use tokio::net::{TcpListener, TcpStream};
439    use tokio_rustls::{TlsAcceptor, TlsConnector};
440
441    fn install_crypto_provider() {
442        rustls::crypto::ring::default_provider()
443            .install_default()
444            .ok();
445    }
446
447    /// Test TLS handshake succeeds with correct fingerprints.
448    #[tokio::test]
449    async fn test_tls_handshake_success_with_matching_fingerprint() {
450        install_crypto_provider();
451        // generate server certificate
452        let server_cert = generate_self_signed_cert().unwrap();
453        let server_config = create_server_config(&server_cert).unwrap();
454        let acceptor = TlsAcceptor::from(server_config);
455        // create client config with correct fingerprint
456        let client_config = create_client_config(server_cert.fingerprint);
457        let connector = TlsConnector::from(client_config);
458        // bind server
459        let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
460        let addr = listener.local_addr().unwrap();
461        // spawn server task
462        let server_acceptor = acceptor.clone();
463        let server_task = tokio::spawn(async move {
464            let (stream, _) = listener.accept().await.unwrap();
465            let mut tls_stream = server_acceptor.accept(stream).await.unwrap();
466            tls_stream.write_all(b"hello").await.unwrap();
467            tls_stream.shutdown().await.unwrap();
468        });
469        // client connects
470        let stream = TcpStream::connect(addr).await.unwrap();
471        let server_name = ServerName::try_from("rcp").unwrap();
472        let mut tls_stream = connector.connect(server_name, stream).await.unwrap();
473        let mut buf = [0u8; 5];
474        tls_stream.read_exact(&mut buf).await.unwrap();
475        assert_eq!(&buf, b"hello");
476        server_task.await.unwrap();
477    }
478
479    /// Test TLS handshake fails when client has wrong server fingerprint.
480    #[tokio::test]
481    async fn test_tls_handshake_fails_with_wrong_server_fingerprint() {
482        install_crypto_provider();
483        // generate server certificate
484        let server_cert = generate_self_signed_cert().unwrap();
485        let server_config = create_server_config(&server_cert).unwrap();
486        let acceptor = TlsAcceptor::from(server_config);
487        // create client config with WRONG fingerprint
488        let wrong_fingerprint = [0xAB; 32];
489        let client_config = create_client_config(wrong_fingerprint);
490        let connector = TlsConnector::from(client_config);
491        // bind server
492        let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
493        let addr = listener.local_addr().unwrap();
494        // spawn server task (will fail when client rejects cert)
495        let server_acceptor = acceptor.clone();
496        let server_task = tokio::spawn(async move {
497            let (stream, _) = listener.accept().await.unwrap();
498            // server accept may fail when client aborts handshake
499            let _ = server_acceptor.accept(stream).await;
500        });
501        // client connects - should fail due to fingerprint mismatch
502        let stream = TcpStream::connect(addr).await.unwrap();
503        let server_name = ServerName::try_from("rcp").unwrap();
504        let result = connector.connect(server_name, stream).await;
505        assert!(result.is_err(), "expected TLS handshake to fail");
506        let err = result.unwrap_err();
507        // the error should indicate certificate validation failed
508        assert!(
509            err.to_string().contains("certificate")
510                || err.to_string().contains("Certificate")
511                || err.to_string().contains("invalid"),
512            "expected certificate error, got: {}",
513            err
514        );
515        server_task.await.unwrap();
516    }
517
518    /// Test mutual TLS handshake fails when server has wrong client fingerprint.
519    #[tokio::test]
520    async fn test_mutual_tls_fails_with_wrong_client_fingerprint() {
521        install_crypto_provider();
522        // generate server and client certificates
523        let server_cert = generate_self_signed_cert().unwrap();
524        let client_cert = generate_self_signed_cert().unwrap();
525        // server expects WRONG client fingerprint
526        let wrong_fingerprint = [0xCD; 32];
527        let server_config =
528            create_server_config_with_client_auth(&server_cert, wrong_fingerprint).unwrap();
529        let acceptor = TlsAcceptor::from(server_config);
530        // client has correct server fingerprint
531        let client_config =
532            create_client_config_with_cert(&client_cert, server_cert.fingerprint).unwrap();
533        let connector = TlsConnector::from(client_config);
534        // bind server
535        let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
536        let addr = listener.local_addr().unwrap();
537        // spawn server task - will fail when verifying client cert
538        let server_acceptor = acceptor.clone();
539        let server_task = tokio::spawn(async move {
540            let (stream, _) = listener.accept().await.unwrap();
541            let result = server_acceptor.accept(stream).await;
542            assert!(result.is_err(), "expected server to reject client cert");
543        });
544        // client connects
545        let stream = TcpStream::connect(addr).await.unwrap();
546        let server_name = ServerName::try_from("rcp").unwrap();
547        // in TLS 1.3, client cert verification happens after client considers handshake done.
548        // the failure shows up as either: connect() error, or subsequent read/write error.
549        match connector.connect(server_name, stream).await {
550            Ok(mut tls_stream) => {
551                // handshake appeared to succeed from client's view, but server will reject.
552                // try to read - server's rejection will cause connection to fail.
553                let mut buf = [0u8; 1];
554                let read_result = tls_stream.read(&mut buf).await;
555                assert!(
556                    read_result.is_err() || read_result.unwrap() == 0,
557                    "expected read to fail or return EOF after server rejection"
558                );
559            }
560            Err(_) => {
561                // handshake failed directly - also acceptable
562            }
563        }
564        server_task.await.unwrap();
565    }
566
567    /// Test mutual TLS handshake succeeds with correct fingerprints.
568    #[tokio::test]
569    async fn test_mutual_tls_success_with_matching_fingerprints() {
570        install_crypto_provider();
571        // generate server and client certificates
572        let server_cert = generate_self_signed_cert().unwrap();
573        let client_cert = generate_self_signed_cert().unwrap();
574        // server expects correct client fingerprint
575        let server_config =
576            create_server_config_with_client_auth(&server_cert, client_cert.fingerprint).unwrap();
577        let acceptor = TlsAcceptor::from(server_config);
578        // client has correct server fingerprint
579        let client_config =
580            create_client_config_with_cert(&client_cert, server_cert.fingerprint).unwrap();
581        let connector = TlsConnector::from(client_config);
582        // bind server
583        let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
584        let addr = listener.local_addr().unwrap();
585        // spawn server task
586        let server_acceptor = acceptor.clone();
587        let server_task = tokio::spawn(async move {
588            let (stream, _) = listener.accept().await.unwrap();
589            let mut tls_stream = server_acceptor.accept(stream).await.unwrap();
590            tls_stream.write_all(b"mutual").await.unwrap();
591            tls_stream.shutdown().await.unwrap();
592        });
593        // client connects
594        let stream = TcpStream::connect(addr).await.unwrap();
595        let server_name = ServerName::try_from("rcp").unwrap();
596        let mut tls_stream = connector.connect(server_name, stream).await.unwrap();
597        let mut buf = [0u8; 6];
598        tls_stream.read_exact(&mut buf).await.unwrap();
599        assert_eq!(&buf, b"mutual");
600        server_task.await.unwrap();
601    }
602
603    /// A peer that replays the pinned certificate's bytes but does NOT hold its private key must
604    /// fail the handshake. The certificate presented matches the pinned fingerprint, but the
605    /// `CertificateVerify` signature is produced with a different key, so proof-of-possession must
606    /// reject it. Guards against regressing the signature verifiers back to unconditional
607    /// acceptance (which would reduce authentication to "presented a known certificate").
608    #[tokio::test]
609    async fn test_tls_handshake_fails_when_cert_replayed_without_private_key() {
610        install_crypto_provider();
611        // the legitimate server certificate the client pins
612        let legit_cert = generate_self_signed_cert().unwrap();
613        // a separate certificate whose private key the attacker actually controls
614        let attacker_cert = generate_self_signed_cert().unwrap();
615        // present the legit certificate but sign with the attacker's key. building the
616        // `CertifiedKey` directly (rather than via `with_single_cert`) bypasses the cert/key
617        // consistency check, mimicking a hand-crafted malicious peer.
618        let attacker_signing_key = rustls::crypto::ring::sign::any_supported_type(
619            &PrivateKeyDer::try_from(attacker_cert.key_der.clone()).unwrap(),
620        )
621        .unwrap();
622        let replayed = std::sync::Arc::new(rustls::sign::CertifiedKey::new(
623            vec![CertificateDer::from(legit_cert.cert_der.clone())],
624            attacker_signing_key,
625        ));
626        #[derive(Debug)]
627        struct StaticResolver(std::sync::Arc<rustls::sign::CertifiedKey>);
628        impl rustls::server::ResolvesServerCert for StaticResolver {
629            fn resolve(
630                &self,
631                _client_hello: rustls::server::ClientHello<'_>,
632            ) -> Option<std::sync::Arc<rustls::sign::CertifiedKey>> {
633                Some(self.0.clone())
634            }
635        }
636        let server_config = ServerConfig::builder()
637            .with_no_client_auth()
638            .with_cert_resolver(std::sync::Arc::new(StaticResolver(replayed)));
639        let acceptor = TlsAcceptor::from(std::sync::Arc::new(server_config));
640        // client pins the legit certificate's fingerprint, which the presented cert matches
641        let client_config = create_client_config(legit_cert.fingerprint);
642        let connector = TlsConnector::from(client_config);
643        let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
644        let addr = listener.local_addr().unwrap();
645        let server_acceptor = acceptor.clone();
646        let server_task = tokio::spawn(async move {
647            let (stream, _) = listener.accept().await.unwrap();
648            // handshake aborts once the client rejects the CertificateVerify signature
649            let _ = server_acceptor.accept(stream).await;
650        });
651        let stream = TcpStream::connect(addr).await.unwrap();
652        let server_name = ServerName::try_from("rcp").unwrap();
653        let result = connector.connect(server_name, stream).await;
654        assert!(
655            result.is_err(),
656            "handshake must fail: cert fingerprint matched but the peer did not prove possession \
657             of the private key"
658        );
659        server_task.await.unwrap();
660    }
661
662    /// The mutual-TLS mirror of the previous test, for the client-certificate path: a client that
663    /// replays the pinned client certificate's bytes but signs with a different key must be
664    /// rejected by the server's client-cert verifier. Guards the source<->destination and
665    /// master<->rcpd client-auth direction.
666    #[tokio::test]
667    async fn test_mutual_tls_fails_when_client_cert_replayed_without_private_key() {
668        install_crypto_provider();
669        let server_cert = generate_self_signed_cert().unwrap();
670        // the legitimate client certificate the server pins
671        let legit_client_cert = generate_self_signed_cert().unwrap();
672        // a separate certificate whose private key the attacker actually controls
673        let attacker_cert = generate_self_signed_cert().unwrap();
674        // server requires a client certificate matching the legit client's fingerprint
675        let server_config =
676            create_server_config_with_client_auth(&server_cert, legit_client_cert.fingerprint)
677                .unwrap();
678        let acceptor = TlsAcceptor::from(server_config);
679        // client presents the legit client certificate but signs with the attacker's key
680        let attacker_signing_key = rustls::crypto::ring::sign::any_supported_type(
681            &PrivateKeyDer::try_from(attacker_cert.key_der.clone()).unwrap(),
682        )
683        .unwrap();
684        let replayed = std::sync::Arc::new(rustls::sign::CertifiedKey::new(
685            vec![CertificateDer::from(legit_client_cert.cert_der.clone())],
686            attacker_signing_key,
687        ));
688        #[derive(Debug)]
689        struct ReplayResolver(std::sync::Arc<rustls::sign::CertifiedKey>);
690        impl rustls::client::ResolvesClientCert for ReplayResolver {
691            fn resolve(
692                &self,
693                _root_hint_subjects: &[&[u8]],
694                _sigschemes: &[SignatureScheme],
695            ) -> Option<std::sync::Arc<rustls::sign::CertifiedKey>> {
696                Some(self.0.clone())
697            }
698            fn has_certs(&self) -> bool {
699                true
700            }
701        }
702        // client pins the real server certificate (so the server side is authenticated normally)
703        let server_verifier =
704            std::sync::Arc::new(FingerprintServerCertVerifier::new(server_cert.fingerprint));
705        let client_config = ClientConfig::builder()
706            .dangerous()
707            .with_custom_certificate_verifier(server_verifier)
708            .with_client_cert_resolver(std::sync::Arc::new(ReplayResolver(replayed)));
709        let connector = TlsConnector::from(std::sync::Arc::new(client_config));
710        let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
711        let addr = listener.local_addr().unwrap();
712        let server_acceptor = acceptor.clone();
713        let server_task = tokio::spawn(async move {
714            let (stream, _) = listener.accept().await.unwrap();
715            let result = server_acceptor.accept(stream).await;
716            assert!(
717                result.is_err(),
718                "server must reject a client that replayed the pinned certificate without its key"
719            );
720        });
721        let stream = TcpStream::connect(addr).await.unwrap();
722        let server_name = ServerName::try_from("rcp").unwrap();
723        // in TLS 1.3 the server verifies the client cert after the client's flight, so the
724        // rejection surfaces as either a connect() error or a subsequent read error. if connect()
725        // fails outright that already proves rejection; otherwise the first read must fail/EOF
726        if let Ok(mut tls_stream) = connector.connect(server_name, stream).await {
727            let mut buf = [0u8; 1];
728            let read_result = tls_stream.read(&mut buf).await;
729            assert!(
730                read_result.is_err() || read_result.unwrap() == 0,
731                "expected read to fail or EOF after the server rejects the replayed client cert"
732            );
733        }
734        server_task.await.unwrap();
735    }
736
737    /// TLS 1.3 is pinned: a peer that offers only TLS 1.2 cannot complete a handshake. Guards the
738    /// version pin so a downgrade to 1.2 (whose separate signature callbacks would otherwise
739    /// apply) cannot be silently reintroduced.
740    #[tokio::test]
741    async fn test_tls_handshake_fails_when_peer_offers_only_tls12() {
742        install_crypto_provider();
743        let server_cert = generate_self_signed_cert().unwrap();
744        // production server config, pinned to TLS 1.3
745        let server_config = create_server_config(&server_cert).unwrap();
746        let acceptor = TlsAcceptor::from(server_config);
747        // client that offers ONLY TLS 1.2
748        let verifier =
749            std::sync::Arc::new(FingerprintServerCertVerifier::new(server_cert.fingerprint));
750        let client_config =
751            ClientConfig::builder_with_protocol_versions(&[&rustls::version::TLS12])
752                .dangerous()
753                .with_custom_certificate_verifier(verifier)
754                .with_no_client_auth();
755        let connector = TlsConnector::from(std::sync::Arc::new(client_config));
756        let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
757        let addr = listener.local_addr().unwrap();
758        let server_acceptor = acceptor.clone();
759        let server_task = tokio::spawn(async move {
760            let (stream, _) = listener.accept().await.unwrap();
761            // handshake fails: no protocol version in common
762            let _ = server_acceptor.accept(stream).await;
763        });
764        let stream = TcpStream::connect(addr).await.unwrap();
765        let server_name = ServerName::try_from("rcp").unwrap();
766        let result = connector.connect(server_name, stream).await;
767        assert!(
768            result.is_err(),
769            "a server pinned to TLS 1.3 must reject a client that offers only TLS 1.2"
770        );
771        server_task.await.unwrap();
772    }
773}