tonic/transport/channel/
tls.rs1use super::service::TlsConnector;
26use crate::transport::{
27 Error,
28 tls::{Certificate, Identity},
29};
30use http::Uri;
31use std::sync::Arc;
32use std::time::Duration;
33use tokio_rustls::rustls::client::danger::ServerCertVerifier;
34use tokio_rustls::rustls::pki_types::TrustAnchor;
35
36#[derive(Debug, Clone, Default)]
38pub struct ClientTlsConfig {
39 domain: Option<String>,
40 certs: Vec<Certificate>,
41 trust_anchors: Vec<TrustAnchor<'static>>,
42 identity: Option<Identity>,
43 assume_http2: bool,
44 #[cfg(feature = "tls-native-roots")]
45 with_native_roots: bool,
46 #[cfg(feature = "tls-webpki-roots")]
47 with_webpki_roots: bool,
48 use_key_log: bool,
49 timeout: Option<Duration>,
50}
51
52impl ClientTlsConfig {
53 pub fn new() -> Self {
55 Self::default()
56 }
57
58 pub fn domain_name(self, domain_name: impl Into<String>) -> Self {
60 ClientTlsConfig {
61 domain: Some(domain_name.into()),
62 ..self
63 }
64 }
65
66 pub fn ca_certificate(self, ca_certificate: Certificate) -> Self {
68 let mut certs = self.certs;
69 certs.push(ca_certificate);
70 ClientTlsConfig { certs, ..self }
71 }
72
73 pub fn ca_certificates(self, ca_certificates: impl IntoIterator<Item = Certificate>) -> Self {
75 let mut certs = self.certs;
76 certs.extend(ca_certificates);
77 ClientTlsConfig { certs, ..self }
78 }
79
80 pub fn trust_anchor(self, trust_anchor: TrustAnchor<'static>) -> Self {
82 let mut trust_anchors = self.trust_anchors;
83 trust_anchors.push(trust_anchor);
84 ClientTlsConfig {
85 trust_anchors,
86 ..self
87 }
88 }
89
90 pub fn trust_anchors(
92 mut self,
93 trust_anchors: impl IntoIterator<Item = TrustAnchor<'static>>,
94 ) -> Self {
95 self.trust_anchors.extend(trust_anchors);
96 self
97 }
98
99 pub fn identity(self, identity: Identity) -> Self {
101 ClientTlsConfig {
102 identity: Some(identity),
103 ..self
104 }
105 }
106
107 pub fn assume_http2(self, assume_http2: bool) -> Self {
110 ClientTlsConfig {
111 assume_http2,
112 ..self
113 }
114 }
115
116 pub fn use_key_log(self) -> Self {
118 ClientTlsConfig {
119 use_key_log: true,
120 ..self
121 }
122 }
123
124 #[cfg(feature = "tls-native-roots")]
126 pub fn with_native_roots(self) -> Self {
127 ClientTlsConfig {
128 with_native_roots: true,
129 ..self
130 }
131 }
132
133 #[cfg(feature = "tls-webpki-roots")]
135 pub fn with_webpki_roots(self) -> Self {
136 ClientTlsConfig {
137 with_webpki_roots: true,
138 ..self
139 }
140 }
141
142 pub fn with_enabled_roots(self) -> Self {
144 let config = self;
145
146 #[cfg(feature = "tls-native-roots")]
147 let config = config.with_native_roots();
148 #[cfg(feature = "tls-webpki-roots")]
149 let config = config.with_webpki_roots();
150
151 config
152 }
153
154 pub fn timeout(self, timeout: Duration) -> Self {
156 ClientTlsConfig {
157 timeout: Some(timeout),
158 ..self
159 }
160 }
161
162 pub(crate) fn into_tls_connector(self, uri: &Uri) -> Result<TlsConnector, crate::BoxError> {
163 self.build_tls_connector(uri, None)
164 }
165
166 pub(crate) fn into_tls_connector_with_verifier(
167 self,
168 uri: &Uri,
169 verifier: Arc<dyn ServerCertVerifier>,
170 ) -> Result<TlsConnector, crate::BoxError> {
171 self.build_tls_connector(uri, Some(verifier))
172 }
173
174 fn build_tls_connector(
175 self,
176 uri: &Uri,
177 server_cert_verifier: Option<Arc<dyn ServerCertVerifier>>,
178 ) -> Result<TlsConnector, crate::BoxError> {
179 let domain = if let Some(domain) = &self.domain {
180 domain
181 } else {
182 let host = uri.host().ok_or_else(Error::new_invalid_uri)?;
183 if host.starts_with('[') && host.ends_with(']') {
185 &host[1..host.len() - 1]
186 } else {
187 host
188 }
189 };
190 TlsConnector::new(
191 self.certs,
192 self.trust_anchors,
193 self.identity,
194 server_cert_verifier,
195 domain,
196 self.assume_http2,
197 self.use_key_log,
198 self.timeout,
199 #[cfg(feature = "tls-native-roots")]
200 self.with_native_roots,
201 #[cfg(feature = "tls-webpki-roots")]
202 self.with_webpki_roots,
203 )
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
212 fn test_into_tls_connector_with_ipv4() {
213 let config = ClientTlsConfig::new();
214 let uri = "https://192.168.1.1:443".parse::<Uri>().unwrap();
215 config.into_tls_connector(&uri).unwrap();
216 }
217
218 #[test]
219 fn test_into_tls_connector_with_ipv6_() {
220 let config = ClientTlsConfig::new();
221 let uri = "https://[::1]:443".parse::<Uri>().unwrap();
222
223 config.into_tls_connector(&uri).unwrap();
224 }
225
226 #[test]
227 fn test_into_tls_connector_with_domain_name() {
228 let config = ClientTlsConfig::new();
229 let uri = "https://example.com:443".parse::<Uri>().unwrap();
230
231 config.into_tls_connector(&uri).unwrap();
232 }
233
234 #[test]
235 fn test_into_tls_connector_with_explicit_domain() {
236 let config = ClientTlsConfig::new().domain_name("example.com");
237 let uri = "https://[2001:db8::1]:443".parse::<Uri>().unwrap();
238
239 config.into_tls_connector(&uri).unwrap();
240 }
241
242 #[test]
243 fn test_into_tls_connector_with_verifier_with_ipv6() {
244 use tokio_rustls::rustls::DigitallySignedStruct;
245 use tokio_rustls::rustls::client::danger::{
246 HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier,
247 };
248 use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName, UnixTime};
249
250 #[derive(Debug)]
251 struct DummyVerifier;
252
253 impl ServerCertVerifier for DummyVerifier {
254 fn verify_server_cert(
255 &self,
256 _end_entity: &CertificateDer<'_>,
257 _intermediates: &[CertificateDer<'_>],
258 _server_name: &ServerName<'_>,
259 _ocsp_response: &[u8],
260 _now: UnixTime,
261 ) -> Result<ServerCertVerified, tokio_rustls::rustls::Error> {
262 Ok(ServerCertVerified::assertion())
263 }
264
265 fn verify_tls12_signature(
266 &self,
267 _message: &[u8],
268 _cert: &CertificateDer<'_>,
269 _dss: &DigitallySignedStruct,
270 ) -> Result<HandshakeSignatureValid, tokio_rustls::rustls::Error> {
271 Ok(HandshakeSignatureValid::assertion())
272 }
273
274 fn verify_tls13_signature(
275 &self,
276 _message: &[u8],
277 _cert: &CertificateDer<'_>,
278 _dss: &DigitallySignedStruct,
279 ) -> Result<HandshakeSignatureValid, tokio_rustls::rustls::Error> {
280 Ok(HandshakeSignatureValid::assertion())
281 }
282
283 fn supported_verify_schemes(&self) -> Vec<tokio_rustls::rustls::SignatureScheme> {
284 tokio_rustls::rustls::crypto::ring::default_provider()
285 .signature_verification_algorithms
286 .supported_schemes()
287 }
288 }
289
290 let config = ClientTlsConfig::new();
291 let uri = "https://[::1]:443".parse::<Uri>().unwrap();
292 config
293 .into_tls_connector_with_verifier(&uri, Arc::new(DummyVerifier))
294 .unwrap();
295 }
296}