agentmux_srv/backend/viewer/
client.rs1use std::sync::Arc;
14use std::time::Duration;
15
16use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
17use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
18use rustls::{DigitallySignedStruct, SignatureScheme};
19
20#[derive(Debug)]
22pub struct Pinned(pub String);
23
24impl ServerCertVerifier for Pinned {
25 fn verify_server_cert(
26 &self,
27 end_entity: &CertificateDer<'_>,
28 _intermediates: &[CertificateDer<'_>],
29 _server_name: &ServerName<'_>,
30 _ocsp: &[u8],
31 _now: UnixTime,
32 ) -> Result<ServerCertVerified, rustls::Error> {
33 let got = super::cert::fingerprint(end_entity);
34 if agentmux_common::secret_eq::secret_eq(got.as_bytes(), self.0.to_ascii_lowercase().as_bytes()) {
35 Ok(ServerCertVerified::assertion())
36 } else {
37 Err(rustls::Error::General("certificate fingerprint mismatch".into()))
38 }
39 }
40
41 fn verify_tls12_signature(
42 &self,
43 message: &[u8],
44 cert: &CertificateDer<'_>,
45 dss: &DigitallySignedStruct,
46 ) -> Result<HandshakeSignatureValid, rustls::Error> {
47 let algs = rustls::crypto::ring::default_provider().signature_verification_algorithms;
48 rustls::crypto::verify_tls12_signature(message, cert, dss, &algs)
49 }
50
51 fn verify_tls13_signature(
52 &self,
53 message: &[u8],
54 cert: &CertificateDer<'_>,
55 dss: &DigitallySignedStruct,
56 ) -> Result<HandshakeSignatureValid, rustls::Error> {
57 let algs = rustls::crypto::ring::default_provider().signature_verification_algorithms;
58 rustls::crypto::verify_tls13_signature(message, cert, dss, &algs)
59 }
60
61 fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
62 rustls::crypto::ring::default_provider().signature_verification_algorithms.supported_schemes()
63 }
64}
65
66pub fn pinned_client(fingerprint: &str, timeout: Duration) -> Result<reqwest::Client, String> {
69 let provider = Arc::new(rustls::crypto::ring::default_provider());
70 let config = rustls::ClientConfig::builder_with_provider(provider)
71 .with_safe_default_protocol_versions()
72 .map_err(|e| e.to_string())?
73 .dangerous()
74 .with_custom_certificate_verifier(Arc::new(Pinned(fingerprint.to_string())))
75 .with_no_client_auth();
76 reqwest::Client::builder()
77 .use_preconfigured_tls(config)
78 .no_proxy()
81 .timeout(timeout)
82 .build()
83 .map_err(|e| e.to_string())
84}