1 | //! TLS configuration and types |
2 | //! |
3 | //! A `Client` will use transport layer security (TLS) by default to connect to |
4 | //! HTTPS destinations. |
5 | //! |
6 | //! # Backends |
7 | //! |
8 | //! reqwest supports several TLS backends, enabled with Cargo features. |
9 | //! |
10 | //! ## default-tls |
11 | //! |
12 | //! reqwest will pick a TLS backend by default. This is true when the |
13 | //! `default-tls` feature is enabled. |
14 | //! |
15 | //! While it currently uses `native-tls`, the feature set is designed to only |
16 | //! enable configuration that is shared among available backends. This allows |
17 | //! reqwest to change the default to `rustls` (or another) at some point in the |
18 | //! future. |
19 | //! |
20 | //! <div class="warning">This feature is enabled by default, and takes |
21 | //! precedence if any other crate enables it. This is true even if you declare |
22 | //! `features = []`. You must set `default-features = false` instead.</div> |
23 | //! |
24 | //! Since Cargo features are additive, other crates in your dependency tree can |
25 | //! cause the default backend to be enabled. If you wish to ensure your |
26 | //! `Client` uses a specific backend, call the appropriate builder methods |
27 | //! (such as [`use_rustls_tls()`][]). |
28 | //! |
29 | //! [`use_rustls_tls()`]: crate::ClientBuilder::use_rustls_tls() |
30 | //! |
31 | //! ## native-tls |
32 | //! |
33 | //! This backend uses the [native-tls][] crate. That will try to use the system |
34 | //! TLS on Windows and Mac, and OpenSSL on Linux targets. |
35 | //! |
36 | //! Enabling the feature explicitly allows for `native-tls`-specific |
37 | //! configuration options. |
38 | //! |
39 | //! [native-tls]: https://crates.io/crates/native-tls |
40 | //! |
41 | //! ## rustls-tls |
42 | //! |
43 | //! This backend uses the [rustls][] crate, a TLS library written in Rust. |
44 | //! |
45 | //! [rustls]: https://crates.io/crates/rustls |
46 | |
47 | #[cfg (feature = "__rustls" )] |
48 | use rustls::{ |
49 | client::danger::HandshakeSignatureValid, client::danger::ServerCertVerified, |
50 | client::danger::ServerCertVerifier, crypto::WebPkiSupportedAlgorithms, |
51 | server::ParsedCertificate, DigitallySignedStruct, Error as TLSError, RootCertStore, |
52 | SignatureScheme, |
53 | }; |
54 | #[cfg (feature = "__rustls" )] |
55 | use rustls_pki_types::{ServerName, UnixTime}; |
56 | use std::{ |
57 | fmt, |
58 | io::{BufRead, BufReader}, |
59 | }; |
60 | |
61 | /// Represents a X509 certificate revocation list. |
62 | #[cfg (feature = "__rustls" )] |
63 | pub struct CertificateRevocationList { |
64 | #[cfg (feature = "__rustls" )] |
65 | inner: rustls_pki_types::CertificateRevocationListDer<'static>, |
66 | } |
67 | |
68 | /// Represents a server X509 certificate. |
69 | #[derive (Clone)] |
70 | pub struct Certificate { |
71 | #[cfg (feature = "default-tls" )] |
72 | native: native_tls_crate::Certificate, |
73 | #[cfg (feature = "__rustls" )] |
74 | original: Cert, |
75 | } |
76 | |
77 | #[cfg (feature = "__rustls" )] |
78 | #[derive (Clone)] |
79 | enum Cert { |
80 | Der(Vec<u8>), |
81 | Pem(Vec<u8>), |
82 | } |
83 | |
84 | /// Represents a private key and X509 cert as a client certificate. |
85 | #[derive (Clone)] |
86 | pub struct Identity { |
87 | #[cfg_attr (not(any(feature = "native-tls" , feature = "__rustls" )), allow(unused))] |
88 | inner: ClientCert, |
89 | } |
90 | |
91 | enum ClientCert { |
92 | #[cfg (feature = "native-tls" )] |
93 | Pkcs12(native_tls_crate::Identity), |
94 | #[cfg (feature = "native-tls" )] |
95 | Pkcs8(native_tls_crate::Identity), |
96 | #[cfg (feature = "__rustls" )] |
97 | Pem { |
98 | key: rustls_pki_types::PrivateKeyDer<'static>, |
99 | certs: Vec<rustls_pki_types::CertificateDer<'static>>, |
100 | }, |
101 | } |
102 | |
103 | impl Clone for ClientCert { |
104 | fn clone(&self) -> Self { |
105 | match self { |
106 | #[cfg (feature = "native-tls" )] |
107 | Self::Pkcs8(i) => Self::Pkcs8(i.clone()), |
108 | #[cfg (feature = "native-tls" )] |
109 | Self::Pkcs12(i) => Self::Pkcs12(i.clone()), |
110 | #[cfg (feature = "__rustls" )] |
111 | ClientCert::Pem { key, certs } => ClientCert::Pem { |
112 | key: key.clone_key(), |
113 | certs: certs.clone(), |
114 | }, |
115 | #[cfg_attr ( |
116 | any(feature = "native-tls" , feature = "__rustls" ), |
117 | allow(unreachable_patterns) |
118 | )] |
119 | _ => unreachable!(), |
120 | } |
121 | } |
122 | } |
123 | |
124 | impl Certificate { |
125 | /// Create a `Certificate` from a binary DER encoded certificate |
126 | /// |
127 | /// # Examples |
128 | /// |
129 | /// ``` |
130 | /// # use std::fs::File; |
131 | /// # use std::io::Read; |
132 | /// # fn cert() -> Result<(), Box<dyn std::error::Error>> { |
133 | /// let mut buf = Vec::new(); |
134 | /// File::open("my_cert.der" )? |
135 | /// .read_to_end(&mut buf)?; |
136 | /// let cert = reqwest::Certificate::from_der(&buf)?; |
137 | /// # drop(cert); |
138 | /// # Ok(()) |
139 | /// # } |
140 | /// ``` |
141 | pub fn from_der(der: &[u8]) -> crate::Result<Certificate> { |
142 | Ok(Certificate { |
143 | #[cfg (feature = "default-tls" )] |
144 | native: native_tls_crate::Certificate::from_der(der).map_err(crate::error::builder)?, |
145 | #[cfg (feature = "__rustls" )] |
146 | original: Cert::Der(der.to_owned()), |
147 | }) |
148 | } |
149 | |
150 | /// Create a `Certificate` from a PEM encoded certificate |
151 | /// |
152 | /// # Examples |
153 | /// |
154 | /// ``` |
155 | /// # use std::fs::File; |
156 | /// # use std::io::Read; |
157 | /// # fn cert() -> Result<(), Box<dyn std::error::Error>> { |
158 | /// let mut buf = Vec::new(); |
159 | /// File::open("my_cert.pem" )? |
160 | /// .read_to_end(&mut buf)?; |
161 | /// let cert = reqwest::Certificate::from_pem(&buf)?; |
162 | /// # drop(cert); |
163 | /// # Ok(()) |
164 | /// # } |
165 | /// ``` |
166 | pub fn from_pem(pem: &[u8]) -> crate::Result<Certificate> { |
167 | Ok(Certificate { |
168 | #[cfg (feature = "default-tls" )] |
169 | native: native_tls_crate::Certificate::from_pem(pem).map_err(crate::error::builder)?, |
170 | #[cfg (feature = "__rustls" )] |
171 | original: Cert::Pem(pem.to_owned()), |
172 | }) |
173 | } |
174 | |
175 | /// Create a collection of `Certificate`s from a PEM encoded certificate bundle. |
176 | /// Example byte sources may be `.crt`, `.cer` or `.pem` files. |
177 | /// |
178 | /// # Examples |
179 | /// |
180 | /// ``` |
181 | /// # use std::fs::File; |
182 | /// # use std::io::Read; |
183 | /// # fn cert() -> Result<(), Box<dyn std::error::Error>> { |
184 | /// let mut buf = Vec::new(); |
185 | /// File::open("ca-bundle.crt" )? |
186 | /// .read_to_end(&mut buf)?; |
187 | /// let certs = reqwest::Certificate::from_pem_bundle(&buf)?; |
188 | /// # drop(certs); |
189 | /// # Ok(()) |
190 | /// # } |
191 | /// ``` |
192 | pub fn from_pem_bundle(pem_bundle: &[u8]) -> crate::Result<Vec<Certificate>> { |
193 | let mut reader = BufReader::new(pem_bundle); |
194 | |
195 | Self::read_pem_certs(&mut reader)? |
196 | .iter() |
197 | .map(|cert_vec| Certificate::from_der(cert_vec)) |
198 | .collect::<crate::Result<Vec<Certificate>>>() |
199 | } |
200 | |
201 | #[cfg (feature = "default-tls" )] |
202 | pub(crate) fn add_to_native_tls(self, tls: &mut native_tls_crate::TlsConnectorBuilder) { |
203 | tls.add_root_certificate(self.native); |
204 | } |
205 | |
206 | #[cfg (feature = "__rustls" )] |
207 | pub(crate) fn add_to_rustls( |
208 | self, |
209 | root_cert_store: &mut rustls::RootCertStore, |
210 | ) -> crate::Result<()> { |
211 | use std::io::Cursor; |
212 | |
213 | match self.original { |
214 | Cert::Der(buf) => root_cert_store |
215 | .add(buf.into()) |
216 | .map_err(crate::error::builder)?, |
217 | Cert::Pem(buf) => { |
218 | let mut reader = Cursor::new(buf); |
219 | let certs = Self::read_pem_certs(&mut reader)?; |
220 | for c in certs { |
221 | root_cert_store |
222 | .add(c.into()) |
223 | .map_err(crate::error::builder)?; |
224 | } |
225 | } |
226 | } |
227 | Ok(()) |
228 | } |
229 | |
230 | fn read_pem_certs(reader: &mut impl BufRead) -> crate::Result<Vec<Vec<u8>>> { |
231 | rustls_pemfile::certs(reader) |
232 | .map(|result| match result { |
233 | Ok(cert) => Ok(cert.as_ref().to_vec()), |
234 | Err(_) => Err(crate::error::builder("invalid certificate encoding" )), |
235 | }) |
236 | .collect() |
237 | } |
238 | } |
239 | |
240 | impl Identity { |
241 | /// Parses a DER-formatted PKCS #12 archive, using the specified password to decrypt the key. |
242 | /// |
243 | /// The archive should contain a leaf certificate and its private key, as well any intermediate |
244 | /// certificates that allow clients to build a chain to a trusted root. |
245 | /// The chain certificates should be in order from the leaf certificate towards the root. |
246 | /// |
247 | /// PKCS #12 archives typically have the file extension `.p12` or `.pfx`, and can be created |
248 | /// with the OpenSSL `pkcs12` tool: |
249 | /// |
250 | /// ```bash |
251 | /// openssl pkcs12 -export -out identity.pfx -inkey key.pem -in cert.pem -certfile chain_certs.pem |
252 | /// ``` |
253 | /// |
254 | /// # Examples |
255 | /// |
256 | /// ``` |
257 | /// # use std::fs::File; |
258 | /// # use std::io::Read; |
259 | /// # fn pkcs12() -> Result<(), Box<dyn std::error::Error>> { |
260 | /// let mut buf = Vec::new(); |
261 | /// File::open("my-ident.pfx")? |
262 | /// .read_to_end(&mut buf)?; |
263 | /// let pkcs12 = reqwest::Identity::from_pkcs12_der(&buf, "my-privkey-password")?; |
264 | /// # drop(pkcs12); |
265 | /// # Ok(()) |
266 | /// # } |
267 | /// ``` |
268 | /// |
269 | /// # Optional |
270 | /// |
271 | /// This requires the `native-tls` Cargo feature enabled. |
272 | #[cfg (feature = "native-tls" )] |
273 | pub fn from_pkcs12_der(der: &[u8], password: &str) -> crate::Result<Identity> { |
274 | Ok(Identity { |
275 | inner: ClientCert::Pkcs12( |
276 | native_tls_crate::Identity::from_pkcs12(der, password) |
277 | .map_err(crate::error::builder)?, |
278 | ), |
279 | }) |
280 | } |
281 | |
282 | /// Parses a chain of PEM encoded X509 certificates, with the leaf certificate first. |
283 | /// `key` is a PEM encoded PKCS #8 formatted private key for the leaf certificate. |
284 | /// |
285 | /// The certificate chain should contain any intermediate certificates that should be sent to |
286 | /// clients to allow them to build a chain to a trusted root. |
287 | /// |
288 | /// A certificate chain here means a series of PEM encoded certificates concatenated together. |
289 | /// |
290 | /// # Examples |
291 | /// |
292 | /// ``` |
293 | /// # use std::fs; |
294 | /// # fn pkcs8() -> Result<(), Box<dyn std::error::Error>> { |
295 | /// let cert = fs::read("client.pem")?; |
296 | /// let key = fs::read("key.pem")?; |
297 | /// let pkcs8 = reqwest::Identity::from_pkcs8_pem(&cert, &key)?; |
298 | /// # drop(pkcs8); |
299 | /// # Ok(()) |
300 | /// # } |
301 | /// ``` |
302 | /// |
303 | /// # Optional |
304 | /// |
305 | /// This requires the `native-tls` Cargo feature enabled. |
306 | #[cfg (feature = "native-tls" )] |
307 | pub fn from_pkcs8_pem(pem: &[u8], key: &[u8]) -> crate::Result<Identity> { |
308 | Ok(Identity { |
309 | inner: ClientCert::Pkcs8( |
310 | native_tls_crate::Identity::from_pkcs8(pem, key).map_err(crate::error::builder)?, |
311 | ), |
312 | }) |
313 | } |
314 | |
315 | /// Parses PEM encoded private key and certificate. |
316 | /// |
317 | /// The input should contain a PEM encoded private key |
318 | /// and at least one PEM encoded certificate. |
319 | /// |
320 | /// Note: The private key must be in RSA, SEC1 Elliptic Curve or PKCS#8 format. |
321 | /// |
322 | /// # Examples |
323 | /// |
324 | /// ``` |
325 | /// # use std::fs::File; |
326 | /// # use std::io::Read; |
327 | /// # fn pem() -> Result<(), Box<dyn std::error::Error>> { |
328 | /// let mut buf = Vec::new(); |
329 | /// File::open("my-ident.pem")? |
330 | /// .read_to_end(&mut buf)?; |
331 | /// let id = reqwest::Identity::from_pem(&buf)?; |
332 | /// # drop(id); |
333 | /// # Ok(()) |
334 | /// # } |
335 | /// ``` |
336 | /// |
337 | /// # Optional |
338 | /// |
339 | /// This requires the `rustls-tls(-...)` Cargo feature enabled. |
340 | #[cfg (feature = "__rustls" )] |
341 | pub fn from_pem(buf: &[u8]) -> crate::Result<Identity> { |
342 | use rustls_pemfile::Item; |
343 | use std::io::Cursor; |
344 | |
345 | let (key, certs) = { |
346 | let mut pem = Cursor::new(buf); |
347 | let mut sk = Vec::<rustls_pki_types::PrivateKeyDer>::new(); |
348 | let mut certs = Vec::<rustls_pki_types::CertificateDer>::new(); |
349 | |
350 | for result in rustls_pemfile::read_all(&mut pem) { |
351 | match result { |
352 | Ok(Item::X509Certificate(cert)) => certs.push(cert), |
353 | Ok(Item::Pkcs1Key(key)) => sk.push(key.into()), |
354 | Ok(Item::Pkcs8Key(key)) => sk.push(key.into()), |
355 | Ok(Item::Sec1Key(key)) => sk.push(key.into()), |
356 | Ok(_) => { |
357 | return Err(crate::error::builder(TLSError::General(String::from( |
358 | "No valid certificate was found" , |
359 | )))) |
360 | } |
361 | Err(_) => { |
362 | return Err(crate::error::builder(TLSError::General(String::from( |
363 | "Invalid identity PEM file" , |
364 | )))) |
365 | } |
366 | } |
367 | } |
368 | |
369 | if let (Some(sk), false) = (sk.pop(), certs.is_empty()) { |
370 | (sk, certs) |
371 | } else { |
372 | return Err(crate::error::builder(TLSError::General(String::from( |
373 | "private key or certificate not found" , |
374 | )))); |
375 | } |
376 | }; |
377 | |
378 | Ok(Identity { |
379 | inner: ClientCert::Pem { key, certs }, |
380 | }) |
381 | } |
382 | |
383 | #[cfg (feature = "native-tls" )] |
384 | pub(crate) fn add_to_native_tls( |
385 | self, |
386 | tls: &mut native_tls_crate::TlsConnectorBuilder, |
387 | ) -> crate::Result<()> { |
388 | match self.inner { |
389 | ClientCert::Pkcs12(id) | ClientCert::Pkcs8(id) => { |
390 | tls.identity(id); |
391 | Ok(()) |
392 | } |
393 | #[cfg (feature = "__rustls" )] |
394 | ClientCert::Pem { .. } => Err(crate::error::builder("incompatible TLS identity type" )), |
395 | } |
396 | } |
397 | |
398 | #[cfg (feature = "__rustls" )] |
399 | pub(crate) fn add_to_rustls( |
400 | self, |
401 | config_builder: rustls::ConfigBuilder< |
402 | rustls::ClientConfig, |
403 | // Not sure here |
404 | rustls::client::WantsClientCert, |
405 | >, |
406 | ) -> crate::Result<rustls::ClientConfig> { |
407 | match self.inner { |
408 | ClientCert::Pem { key, certs } => config_builder |
409 | .with_client_auth_cert(certs, key) |
410 | .map_err(crate::error::builder), |
411 | #[cfg (feature = "native-tls" )] |
412 | ClientCert::Pkcs12(..) | ClientCert::Pkcs8(..) => { |
413 | Err(crate::error::builder("incompatible TLS identity type" )) |
414 | } |
415 | } |
416 | } |
417 | } |
418 | |
419 | #[cfg (feature = "__rustls" )] |
420 | impl CertificateRevocationList { |
421 | /// Parses a PEM encoded CRL. |
422 | /// |
423 | /// # Examples |
424 | /// |
425 | /// ``` |
426 | /// # use std::fs::File; |
427 | /// # use std::io::Read; |
428 | /// # fn crl() -> Result<(), Box<dyn std::error::Error>> { |
429 | /// let mut buf = Vec::new(); |
430 | /// File::open("my_crl.pem")? |
431 | /// .read_to_end(&mut buf)?; |
432 | /// let crl = reqwest::tls::CertificateRevocationList::from_pem(&buf)?; |
433 | /// # drop(crl); |
434 | /// # Ok(()) |
435 | /// # } |
436 | /// ``` |
437 | /// |
438 | /// # Optional |
439 | /// |
440 | /// This requires the `rustls-tls(-...)` Cargo feature enabled. |
441 | #[cfg (feature = "__rustls" )] |
442 | pub fn from_pem(pem: &[u8]) -> crate::Result<CertificateRevocationList> { |
443 | Ok(CertificateRevocationList { |
444 | #[cfg (feature = "__rustls" )] |
445 | inner: rustls_pki_types::CertificateRevocationListDer::from(pem.to_vec()), |
446 | }) |
447 | } |
448 | |
449 | /// Creates a collection of `CertificateRevocationList`s from a PEM encoded CRL bundle. |
450 | /// Example byte sources may be `.crl` or `.pem` files. |
451 | /// |
452 | /// # Examples |
453 | /// |
454 | /// ``` |
455 | /// # use std::fs::File; |
456 | /// # use std::io::Read; |
457 | /// # fn crls() -> Result<(), Box<dyn std::error::Error>> { |
458 | /// let mut buf = Vec::new(); |
459 | /// File::open("crl-bundle.crl")? |
460 | /// .read_to_end(&mut buf)?; |
461 | /// let crls = reqwest::tls::CertificateRevocationList::from_pem_bundle(&buf)?; |
462 | /// # drop(crls); |
463 | /// # Ok(()) |
464 | /// # } |
465 | /// ``` |
466 | /// |
467 | /// # Optional |
468 | /// |
469 | /// This requires the `rustls-tls(-...)` Cargo feature enabled. |
470 | #[cfg (feature = "__rustls" )] |
471 | pub fn from_pem_bundle(pem_bundle: &[u8]) -> crate::Result<Vec<CertificateRevocationList>> { |
472 | let mut reader = BufReader::new(pem_bundle); |
473 | |
474 | rustls_pemfile::crls(&mut reader) |
475 | .map(|result| match result { |
476 | Ok(crl) => Ok(CertificateRevocationList { inner: crl }), |
477 | Err(_) => Err(crate::error::builder("invalid crl encoding" )), |
478 | }) |
479 | .collect::<crate::Result<Vec<CertificateRevocationList>>>() |
480 | } |
481 | |
482 | #[cfg (feature = "__rustls" )] |
483 | pub(crate) fn as_rustls_crl<'a>(&self) -> rustls_pki_types::CertificateRevocationListDer<'a> { |
484 | self.inner.clone() |
485 | } |
486 | } |
487 | |
488 | impl fmt::Debug for Certificate { |
489 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
490 | f.debug_struct(name:"Certificate" ).finish() |
491 | } |
492 | } |
493 | |
494 | impl fmt::Debug for Identity { |
495 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
496 | f.debug_struct(name:"Identity" ).finish() |
497 | } |
498 | } |
499 | |
500 | #[cfg (feature = "__rustls" )] |
501 | impl fmt::Debug for CertificateRevocationList { |
502 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
503 | f.debug_struct("CertificateRevocationList" ).finish() |
504 | } |
505 | } |
506 | |
507 | /// A TLS protocol version. |
508 | #[derive (Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] |
509 | pub struct Version(InnerVersion); |
510 | |
511 | #[derive (Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] |
512 | #[non_exhaustive ] |
513 | enum InnerVersion { |
514 | Tls1_0, |
515 | Tls1_1, |
516 | Tls1_2, |
517 | Tls1_3, |
518 | } |
519 | |
520 | // These could perhaps be From/TryFrom implementations, but those would be |
521 | // part of the public API so let's be careful |
522 | impl Version { |
523 | /// Version 1.0 of the TLS protocol. |
524 | pub const TLS_1_0: Version = Version(InnerVersion::Tls1_0); |
525 | /// Version 1.1 of the TLS protocol. |
526 | pub const TLS_1_1: Version = Version(InnerVersion::Tls1_1); |
527 | /// Version 1.2 of the TLS protocol. |
528 | pub const TLS_1_2: Version = Version(InnerVersion::Tls1_2); |
529 | /// Version 1.3 of the TLS protocol. |
530 | pub const TLS_1_3: Version = Version(InnerVersion::Tls1_3); |
531 | |
532 | #[cfg (feature = "default-tls" )] |
533 | pub(crate) fn to_native_tls(self) -> Option<native_tls_crate::Protocol> { |
534 | match self.0 { |
535 | InnerVersion::Tls1_0 => Some(native_tls_crate::Protocol::Tlsv10), |
536 | InnerVersion::Tls1_1 => Some(native_tls_crate::Protocol::Tlsv11), |
537 | InnerVersion::Tls1_2 => Some(native_tls_crate::Protocol::Tlsv12), |
538 | InnerVersion::Tls1_3 => None, |
539 | } |
540 | } |
541 | |
542 | #[cfg (feature = "__rustls" )] |
543 | pub(crate) fn from_rustls(version: rustls::ProtocolVersion) -> Option<Self> { |
544 | match version { |
545 | rustls::ProtocolVersion::SSLv2 => None, |
546 | rustls::ProtocolVersion::SSLv3 => None, |
547 | rustls::ProtocolVersion::TLSv1_0 => Some(Self(InnerVersion::Tls1_0)), |
548 | rustls::ProtocolVersion::TLSv1_1 => Some(Self(InnerVersion::Tls1_1)), |
549 | rustls::ProtocolVersion::TLSv1_2 => Some(Self(InnerVersion::Tls1_2)), |
550 | rustls::ProtocolVersion::TLSv1_3 => Some(Self(InnerVersion::Tls1_3)), |
551 | _ => None, |
552 | } |
553 | } |
554 | } |
555 | |
556 | pub(crate) enum TlsBackend { |
557 | // This is the default and HTTP/3 feature does not use it so suppress it. |
558 | #[allow (dead_code)] |
559 | #[cfg (feature = "default-tls" )] |
560 | Default, |
561 | #[cfg (feature = "native-tls" )] |
562 | BuiltNativeTls(native_tls_crate::TlsConnector), |
563 | #[cfg (feature = "__rustls" )] |
564 | Rustls, |
565 | #[cfg (feature = "__rustls" )] |
566 | BuiltRustls(rustls::ClientConfig), |
567 | #[cfg (any(feature = "native-tls" , feature = "__rustls" ,))] |
568 | UnknownPreconfigured, |
569 | } |
570 | |
571 | impl fmt::Debug for TlsBackend { |
572 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
573 | match self { |
574 | #[cfg (feature = "default-tls" )] |
575 | TlsBackend::Default => write!(f, "Default" ), |
576 | #[cfg (feature = "native-tls" )] |
577 | TlsBackend::BuiltNativeTls(_) => write!(f, "BuiltNativeTls" ), |
578 | #[cfg (feature = "__rustls" )] |
579 | TlsBackend::Rustls => write!(f, "Rustls" ), |
580 | #[cfg (feature = "__rustls" )] |
581 | TlsBackend::BuiltRustls(_) => write!(f, "BuiltRustls" ), |
582 | #[cfg (any(feature = "native-tls" , feature = "__rustls" ,))] |
583 | TlsBackend::UnknownPreconfigured => write!(f, "UnknownPreconfigured" ), |
584 | } |
585 | } |
586 | } |
587 | |
588 | #[allow (clippy::derivable_impls)] |
589 | impl Default for TlsBackend { |
590 | fn default() -> TlsBackend { |
591 | #[cfg (all(feature = "default-tls" , not(feature = "http3" )))] |
592 | { |
593 | TlsBackend::Default |
594 | } |
595 | |
596 | #[cfg (any( |
597 | all(feature = "__rustls" , not(feature = "default-tls" )), |
598 | feature = "http3" |
599 | ))] |
600 | { |
601 | TlsBackend::Rustls |
602 | } |
603 | } |
604 | } |
605 | |
606 | #[cfg (feature = "__rustls" )] |
607 | #[derive (Debug)] |
608 | pub(crate) struct NoVerifier; |
609 | |
610 | #[cfg (feature = "__rustls" )] |
611 | impl ServerCertVerifier for NoVerifier { |
612 | fn verify_server_cert( |
613 | &self, |
614 | _end_entity: &rustls_pki_types::CertificateDer, |
615 | _intermediates: &[rustls_pki_types::CertificateDer], |
616 | _server_name: &ServerName, |
617 | _ocsp_response: &[u8], |
618 | _now: UnixTime, |
619 | ) -> Result<ServerCertVerified, TLSError> { |
620 | Ok(ServerCertVerified::assertion()) |
621 | } |
622 | |
623 | fn verify_tls12_signature( |
624 | &self, |
625 | _message: &[u8], |
626 | _cert: &rustls_pki_types::CertificateDer, |
627 | _dss: &DigitallySignedStruct, |
628 | ) -> Result<HandshakeSignatureValid, TLSError> { |
629 | Ok(HandshakeSignatureValid::assertion()) |
630 | } |
631 | |
632 | fn verify_tls13_signature( |
633 | &self, |
634 | _message: &[u8], |
635 | _cert: &rustls_pki_types::CertificateDer, |
636 | _dss: &DigitallySignedStruct, |
637 | ) -> Result<HandshakeSignatureValid, TLSError> { |
638 | Ok(HandshakeSignatureValid::assertion()) |
639 | } |
640 | |
641 | fn supported_verify_schemes(&self) -> Vec<SignatureScheme> { |
642 | vec![ |
643 | SignatureScheme::RSA_PKCS1_SHA1, |
644 | SignatureScheme::ECDSA_SHA1_Legacy, |
645 | SignatureScheme::RSA_PKCS1_SHA256, |
646 | SignatureScheme::ECDSA_NISTP256_SHA256, |
647 | SignatureScheme::RSA_PKCS1_SHA384, |
648 | SignatureScheme::ECDSA_NISTP384_SHA384, |
649 | SignatureScheme::RSA_PKCS1_SHA512, |
650 | SignatureScheme::ECDSA_NISTP521_SHA512, |
651 | SignatureScheme::RSA_PSS_SHA256, |
652 | SignatureScheme::RSA_PSS_SHA384, |
653 | SignatureScheme::RSA_PSS_SHA512, |
654 | SignatureScheme::ED25519, |
655 | SignatureScheme::ED448, |
656 | ] |
657 | } |
658 | } |
659 | |
660 | #[cfg (feature = "__rustls" )] |
661 | #[derive (Debug)] |
662 | pub(crate) struct IgnoreHostname { |
663 | roots: RootCertStore, |
664 | signature_algorithms: WebPkiSupportedAlgorithms, |
665 | } |
666 | |
667 | #[cfg (feature = "__rustls" )] |
668 | impl IgnoreHostname { |
669 | pub(crate) fn new( |
670 | roots: RootCertStore, |
671 | signature_algorithms: WebPkiSupportedAlgorithms, |
672 | ) -> Self { |
673 | Self { |
674 | roots, |
675 | signature_algorithms, |
676 | } |
677 | } |
678 | } |
679 | |
680 | #[cfg (feature = "__rustls" )] |
681 | impl ServerCertVerifier for IgnoreHostname { |
682 | fn verify_server_cert( |
683 | &self, |
684 | end_entity: &rustls_pki_types::CertificateDer<'_>, |
685 | intermediates: &[rustls_pki_types::CertificateDer<'_>], |
686 | _server_name: &ServerName<'_>, |
687 | _ocsp_response: &[u8], |
688 | now: UnixTime, |
689 | ) -> Result<ServerCertVerified, TLSError> { |
690 | let cert = ParsedCertificate::try_from(end_entity)?; |
691 | |
692 | rustls::client::verify_server_cert_signed_by_trust_anchor( |
693 | &cert, |
694 | &self.roots, |
695 | intermediates, |
696 | now, |
697 | self.signature_algorithms.all, |
698 | )?; |
699 | Ok(ServerCertVerified::assertion()) |
700 | } |
701 | |
702 | fn verify_tls12_signature( |
703 | &self, |
704 | message: &[u8], |
705 | cert: &rustls_pki_types::CertificateDer<'_>, |
706 | dss: &DigitallySignedStruct, |
707 | ) -> Result<HandshakeSignatureValid, TLSError> { |
708 | rustls::crypto::verify_tls12_signature(message, cert, dss, &self.signature_algorithms) |
709 | } |
710 | |
711 | fn verify_tls13_signature( |
712 | &self, |
713 | message: &[u8], |
714 | cert: &rustls_pki_types::CertificateDer<'_>, |
715 | dss: &DigitallySignedStruct, |
716 | ) -> Result<HandshakeSignatureValid, TLSError> { |
717 | rustls::crypto::verify_tls13_signature(message, cert, dss, &self.signature_algorithms) |
718 | } |
719 | |
720 | fn supported_verify_schemes(&self) -> Vec<SignatureScheme> { |
721 | self.signature_algorithms.supported_schemes() |
722 | } |
723 | } |
724 | |
725 | /// Hyper extension carrying extra TLS layer information. |
726 | /// Made available to clients on responses when `tls_info` is set. |
727 | #[derive (Clone)] |
728 | pub struct TlsInfo { |
729 | pub(crate) peer_certificate: Option<Vec<u8>>, |
730 | } |
731 | |
732 | impl TlsInfo { |
733 | /// Get the DER encoded leaf certificate of the peer. |
734 | pub fn peer_certificate(&self) -> Option<&[u8]> { |
735 | self.peer_certificate.as_ref().map(|der: &Vec| &der[..]) |
736 | } |
737 | } |
738 | |
739 | impl std::fmt::Debug for TlsInfo { |
740 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
741 | f.debug_struct(name:"TlsInfo" ).finish() |
742 | } |
743 | } |
744 | |
745 | #[cfg (test)] |
746 | mod tests { |
747 | use super::*; |
748 | |
749 | #[cfg (feature = "default-tls" )] |
750 | #[test ] |
751 | fn certificate_from_der_invalid() { |
752 | Certificate::from_der(b"not der" ).unwrap_err(); |
753 | } |
754 | |
755 | #[cfg (feature = "default-tls" )] |
756 | #[test ] |
757 | fn certificate_from_pem_invalid() { |
758 | Certificate::from_pem(b"not pem" ).unwrap_err(); |
759 | } |
760 | |
761 | #[cfg (feature = "native-tls" )] |
762 | #[test ] |
763 | fn identity_from_pkcs12_der_invalid() { |
764 | Identity::from_pkcs12_der(b"not der" , "nope" ).unwrap_err(); |
765 | } |
766 | |
767 | #[cfg (feature = "native-tls" )] |
768 | #[test ] |
769 | fn identity_from_pkcs8_pem_invalid() { |
770 | Identity::from_pkcs8_pem(b"not pem" , b"not key" ).unwrap_err(); |
771 | } |
772 | |
773 | #[cfg (feature = "__rustls" )] |
774 | #[test ] |
775 | fn identity_from_pem_invalid() { |
776 | Identity::from_pem(b"not pem" ).unwrap_err(); |
777 | } |
778 | |
779 | #[cfg (feature = "__rustls" )] |
780 | #[test ] |
781 | fn identity_from_pem_pkcs1_key() { |
782 | let pem = b"-----BEGIN CERTIFICATE----- \n\ |
783 | -----END CERTIFICATE----- \n\ |
784 | -----BEGIN RSA PRIVATE KEY----- \n\ |
785 | -----END RSA PRIVATE KEY----- \n" ; |
786 | |
787 | Identity::from_pem(pem).unwrap(); |
788 | } |
789 | |
790 | #[test ] |
791 | fn certificates_from_pem_bundle() { |
792 | const PEM_BUNDLE: &[u8] = b" |
793 | -----BEGIN CERTIFICATE----- |
794 | MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 |
795 | MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g |
796 | Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG |
797 | A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg |
798 | Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl |
799 | ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j |
800 | QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr |
801 | ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr |
802 | BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM |
803 | YyRIHN8wfdVoOw== |
804 | -----END CERTIFICATE----- |
805 | |
806 | -----BEGIN CERTIFICATE----- |
807 | MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 |
808 | MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g |
809 | Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG |
810 | A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg |
811 | Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi |
812 | 9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk |
813 | M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB |
814 | /zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB |
815 | MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw |
816 | CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW |
817 | 1KyLa2tJElMzrdfkviT8tQp21KW8EA== |
818 | -----END CERTIFICATE----- |
819 | " ; |
820 | |
821 | assert!(Certificate::from_pem_bundle(PEM_BUNDLE).is_ok()) |
822 | } |
823 | |
824 | #[cfg (feature = "__rustls" )] |
825 | #[test ] |
826 | fn crl_from_pem() { |
827 | let pem = b"-----BEGIN X509 CRL----- \n-----END X509 CRL----- \n" ; |
828 | |
829 | CertificateRevocationList::from_pem(pem).unwrap(); |
830 | } |
831 | |
832 | #[cfg (feature = "__rustls" )] |
833 | #[test ] |
834 | fn crl_from_pem_bundle() { |
835 | let pem_bundle = std::fs::read("tests/support/crl.pem" ).unwrap(); |
836 | |
837 | let result = CertificateRevocationList::from_pem_bundle(&pem_bundle); |
838 | |
839 | assert!(result.is_ok()); |
840 | let result = result.unwrap(); |
841 | assert_eq!(result.len(), 1); |
842 | } |
843 | } |
844 | |