1use std::sync::Arc;
2
3use rustls::ClientConfig;
4use rustls::pki_types::CertificateDer;
5
6use super::TlsError;
7
8#[derive(Clone)]
25pub struct TlsConfig {
26 pub(crate) inner: Arc<ClientConfig>,
27}
28
29impl TlsConfig {
30 pub fn new() -> Result<Self, TlsError> {
36 Self::builder().build()
37 }
38
39 pub fn client_config(&self) -> &Arc<ClientConfig> {
44 &self.inner
45 }
46
47 #[must_use]
49 pub fn builder() -> TlsConfigBuilder {
50 TlsConfigBuilder {
51 custom_roots: Vec::new(),
52 skip_system_certs: false,
53 no_verify: false,
54 tls13_only: false,
55 }
56 }
57}
58
59pub struct TlsConfigBuilder {
61 custom_roots: Vec<CertificateDer<'static>>,
62 skip_system_certs: bool,
63 no_verify: bool,
64 tls13_only: bool,
65}
66
67impl TlsConfigBuilder {
68 #[must_use]
72 pub fn add_root_cert(mut self, der: impl Into<CertificateDer<'static>>) -> Self {
73 self.custom_roots.push(der.into());
74 self
75 }
76
77 #[must_use]
81 pub fn skip_system_certs(mut self) -> Self {
82 self.skip_system_certs = true;
83 self
84 }
85
86 #[must_use]
93 pub fn danger_no_verify(mut self) -> Self {
94 self.no_verify = true;
95 self
96 }
97
98 #[must_use]
103 pub fn tls13_only(mut self) -> Self {
104 self.tls13_only = true;
105 self
106 }
107
108 pub fn build(self) -> Result<TlsConfig, TlsError> {
110 let versions: &[&rustls::SupportedProtocolVersion] = if self.tls13_only {
111 &[&rustls::version::TLS13]
112 } else {
113 rustls::ALL_VERSIONS
114 };
115
116 let config = if self.no_verify {
117 ClientConfig::builder_with_protocol_versions(versions)
118 .dangerous()
119 .with_custom_certificate_verifier(Arc::new(NoVerifier))
120 .with_no_client_auth()
121 } else {
122 let mut root_store = rustls::RootCertStore::empty();
123
124 if !self.skip_system_certs {
125 let result = rustls_native_certs::load_native_certs();
126 if result.certs.is_empty() {
127 return Err(TlsError::NoRootCerts);
128 }
129 root_store.add_parsable_certificates(result.certs);
130 }
131
132 for cert in self.custom_roots {
133 root_store.add(cert).map_err(TlsError::Rustls)?;
134 }
135
136 if root_store.is_empty() {
137 return Err(TlsError::NoRootCerts);
138 }
139
140 ClientConfig::builder_with_protocol_versions(versions)
141 .with_root_certificates(root_store)
142 .with_no_client_auth()
143 };
144
145 Ok(TlsConfig {
146 inner: Arc::new(config),
147 })
148 }
149}
150
151#[derive(Debug)]
153struct NoVerifier;
154
155impl rustls::client::danger::ServerCertVerifier for NoVerifier {
156 fn verify_server_cert(
157 &self,
158 _end_entity: &CertificateDer<'_>,
159 _intermediates: &[CertificateDer<'_>],
160 _server_name: &rustls::pki_types::ServerName<'_>,
161 _ocsp_response: &[u8],
162 _now: rustls::pki_types::UnixTime,
163 ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
164 Ok(rustls::client::danger::ServerCertVerified::assertion())
165 }
166
167 fn verify_tls12_signature(
168 &self,
169 _message: &[u8],
170 _cert: &CertificateDer<'_>,
171 _dss: &rustls::DigitallySignedStruct,
172 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
173 Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
174 }
175
176 fn verify_tls13_signature(
177 &self,
178 _message: &[u8],
179 _cert: &CertificateDer<'_>,
180 _dss: &rustls::DigitallySignedStruct,
181 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
182 Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
183 }
184
185 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
186 rustls::crypto::aws_lc_rs::default_provider()
187 .signature_verification_algorithms
188 .supported_schemes()
189 }
190}