rocket/config/
tls.rs

1use figment::value::magic::{Either, RelativePathBuf};
2use serde::{Deserialize, Serialize};
3use indexmap::IndexSet;
4
5/// TLS configuration: certificate chain, key, and ciphersuites.
6///
7/// Four parameters control `tls` configuration:
8///
9///   * `certs`, `key`
10///
11///     Both `certs` and `key` can be configured as a path or as raw bytes.
12///     `certs` must be a DER-encoded X.509 TLS certificate chain, while `key`
13///     must be a DER-encoded ASN.1 key in either PKCS#8, PKCS#1, or SEC1
14///     format. When a path is configured in a file, such as `Rocket.toml`,
15///     relative paths are interpreted as relative to the source file's
16///     directory.
17///
18///   * `ciphers`
19///
20///     A list of supported [`CipherSuite`]s in server-preferred order, from
21///     most to least. It is not required and defaults to
22///     [`CipherSuite::DEFAULT_SET`], the recommended setting.
23///
24///   * `prefer_server_cipher_order`
25///
26///     A boolean that indicates whether the server should regard its own
27///     ciphersuite preferences over the client's. The default and recommended
28///     value is `false`.
29///
30/// Additionally, the `mutual` parameter controls if and how the server
31/// authenticates clients via mutual TLS. It works in concert with the
32/// [`mtls`](crate::mtls) module. See [`MutualTls`] for configuration details.
33///
34/// In `Rocket.toml`, configuration might look like:
35///
36/// ```toml
37/// [default.tls]
38/// certs = "private/rsa_sha256_cert.pem"
39/// key = "private/rsa_sha256_key.pem"
40/// ```
41///
42/// With a custom programmatic configuration, this might look like:
43///
44/// ```rust
45/// # #[macro_use] extern crate rocket;
46/// use rocket::config::{Config, TlsConfig, CipherSuite};
47///
48/// #[launch]
49/// fn rocket() -> _ {
50///     let tls_config = TlsConfig::from_paths("/ssl/certs.pem", "/ssl/key.pem")
51///         .with_ciphers(CipherSuite::TLS_V13_SET)
52///         .with_preferred_server_cipher_order(true);
53///
54///     let config = Config {
55///         tls: Some(tls_config),
56///         ..Default::default()
57///     };
58///
59///     rocket::custom(config)
60/// }
61/// ```
62///
63/// Or by creating a custom figment:
64///
65/// ```rust
66/// use rocket::config::Config;
67///
68/// let figment = Config::figment()
69///     .merge(("tls.certs", "path/to/certs.pem"))
70///     .merge(("tls.key", vec![0; 32]));
71/// #
72/// # let config = rocket::Config::from(figment);
73/// # let tls_config = config.tls.as_ref().unwrap();
74/// # assert!(tls_config.certs().is_left());
75/// # assert!(tls_config.key().is_right());
76/// # assert_eq!(tls_config.ciphers().count(), 9);
77/// # assert!(!tls_config.prefer_server_cipher_order());
78/// ```
79#[derive(PartialEq, Debug, Clone, Deserialize, Serialize)]
80#[cfg_attr(nightly, doc(cfg(feature = "tls")))]
81pub struct TlsConfig {
82    /// Path to a PEM file with, or raw bytes for, a DER-encoded X.509 TLS
83    /// certificate chain.
84    pub(crate) certs: Either<RelativePathBuf, Vec<u8>>,
85    /// Path to a PEM file with, or raw bytes for, DER-encoded private key in
86    /// either PKCS#8 or PKCS#1 format.
87    pub(crate) key: Either<RelativePathBuf, Vec<u8>>,
88    /// List of TLS cipher suites in server-preferred order.
89    #[serde(default = "CipherSuite::default_set")]
90    pub(crate) ciphers: IndexSet<CipherSuite>,
91    /// Whether to prefer the server's cipher suite order over the client's.
92    #[serde(default)]
93    pub(crate) prefer_server_cipher_order: bool,
94    /// Configuration for mutual TLS, if any.
95    #[serde(default)]
96    #[cfg(feature = "mtls")]
97    #[cfg_attr(nightly, doc(cfg(feature = "mtls")))]
98    pub(crate) mutual: Option<MutualTls>,
99}
100
101/// Mutual TLS configuration.
102///
103/// Configuration works in concert with the [`mtls`](crate::mtls) module, which
104/// provides a request guard to validate, verify, and retrieve client
105/// certificates in routes.
106///
107/// By default, mutual TLS is disabled and client certificates are not required,
108/// validated or verified. To enable mutual TLS, the `mtls` feature must be
109/// enabled and support configured via two `tls.mutual` parameters:
110///
111///   * `ca_certs`
112///
113///     A required path to a PEM file or raw bytes to a DER-encoded X.509 TLS
114///     certificate chain for the certificate authority to verify client
115///     certificates against. When a path is configured in a file, such as
116///     `Rocket.toml`, relative paths are interpreted as relative to the source
117///     file's directory.
118///
119///   * `mandatory`
120///
121///     An optional boolean that control whether client authentication is
122///     required.
123///
124///     When `true`, client authentication is required. TLS connections where
125///     the client does not present a certificate are immediately terminated.
126///     When `false`, the client is not required to present a certificate. In
127///     either case, if a certificate _is_ presented, it must be valid or the
128///     connection is terminated.
129///
130/// In a `Rocket.toml`, configuration might look like:
131///
132/// ```toml
133/// [default.tls.mutual]
134/// ca_certs = "/ssl/ca_cert.pem"
135/// mandatory = true                # when absent, defaults to false
136/// ```
137///
138/// Programmatically, configuration might look like:
139///
140/// ```rust
141/// # #[macro_use] extern crate rocket;
142/// use rocket::config::{Config, TlsConfig, MutualTls};
143///
144/// #[launch]
145/// fn rocket() -> _ {
146///     let tls_config = TlsConfig::from_paths("/ssl/certs.pem", "/ssl/key.pem")
147///         .with_mutual(MutualTls::from_path("/ssl/ca_cert.pem"));
148///
149///     let config = Config {
150///         tls: Some(tls_config),
151///         ..Default::default()
152///     };
153///
154///     rocket::custom(config)
155/// }
156/// ```
157///
158/// Once mTLS is configured, the [`mtls::Certificate`](crate::mtls::Certificate)
159/// request guard can be used to retrieve client certificates in routes.
160#[derive(PartialEq, Debug, Clone, Deserialize, Serialize)]
161#[cfg(feature = "mtls")]
162#[cfg_attr(nightly, doc(cfg(feature = "mtls")))]
163pub struct MutualTls {
164    /// Path to a PEM file with, or raw bytes for, DER-encoded Certificate
165    /// Authority certificates which will be used to verify client-presented
166    /// certificates.
167    // TODO: We should support more than one root.
168    pub(crate) ca_certs: Either<RelativePathBuf, Vec<u8>>,
169    /// Whether the client is required to present a certificate.
170    ///
171    /// When `true`, the client is required to present a valid certificate to
172    /// proceed with TLS. When `false`, the client is not required to present a
173    /// certificate. In either case, if a certificate _is_ presented, it must be
174    /// valid or the connection is terminated.
175    #[serde(default)]
176    #[serde(deserialize_with = "figment::util::bool_from_str_or_int")]
177    pub mandatory: bool,
178}
179
180/// A supported TLS cipher suite.
181#[allow(non_camel_case_types)]
182#[derive(PartialEq, Eq, Debug, Copy, Clone, Hash, Deserialize, Serialize)]
183#[cfg_attr(nightly, doc(cfg(feature = "tls")))]
184#[non_exhaustive]
185pub enum CipherSuite {
186    /// The TLS 1.3 `TLS_CHACHA20_POLY1305_SHA256` cipher suite.
187    TLS_CHACHA20_POLY1305_SHA256,
188    /// The TLS 1.3 `TLS_AES_256_GCM_SHA384` cipher suite.
189    TLS_AES_256_GCM_SHA384,
190    /// The TLS 1.3 `TLS_AES_128_GCM_SHA256` cipher suite.
191    TLS_AES_128_GCM_SHA256,
192
193    /// The TLS 1.2 `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256` cipher suite.
194    TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
195    /// The TLS 1.2 `TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256` cipher suite.
196    TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
197    /// The TLS 1.2 `TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384` cipher suite.
198    TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
199    /// The TLS 1.2 `TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256` cipher suite.
200    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
201    /// The TLS 1.2 `TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384` cipher suite.
202    TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
203    /// The TLS 1.2 `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256` cipher suite.
204    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
205}
206
207impl CipherSuite {
208    /// The default set and order of cipher suites. These are all of the
209    /// variants in [`CipherSuite`] in their declaration order.
210    pub const DEFAULT_SET: [CipherSuite; 9] = [
211        // TLS v1.3 suites...
212        CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
213        CipherSuite::TLS_AES_256_GCM_SHA384,
214        CipherSuite::TLS_AES_128_GCM_SHA256,
215
216        // TLS v1.2 suites...
217        CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
218        CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
219        CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
220        CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
221        CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
222        CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
223    ];
224
225    /// The default set and order of cipher suites. These are the TLS 1.3
226    /// variants in [`CipherSuite`] in their declaration order.
227    pub const TLS_V13_SET: [CipherSuite; 3] = [
228        CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
229        CipherSuite::TLS_AES_256_GCM_SHA384,
230        CipherSuite::TLS_AES_128_GCM_SHA256,
231    ];
232
233    /// The default set and order of cipher suites. These are the TLS 1.2
234    /// variants in [`CipherSuite`] in their declaration order.
235    pub const TLS_V12_SET: [CipherSuite; 6] = [
236        CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
237        CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
238        CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
239        CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
240        CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
241        CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
242    ];
243
244    /// Used as the `serde` default for `ciphers`.
245    fn default_set() -> IndexSet<Self> {
246        Self::DEFAULT_SET.iter().copied().collect()
247    }
248}
249
250impl TlsConfig {
251    fn default() -> Self {
252        TlsConfig {
253            certs: Either::Right(vec![]),
254            key: Either::Right(vec![]),
255            ciphers: CipherSuite::default_set(),
256            prefer_server_cipher_order: false,
257            #[cfg(feature = "mtls")]
258            mutual: None,
259        }
260    }
261
262    /// Constructs a `TlsConfig` from paths to a `certs` certificate chain
263    /// a `key` private-key. This method does no validation; it simply creates a
264    /// structure suitable for passing into a [`Config`](crate::Config).
265    ///
266    /// # Example
267    ///
268    /// ```rust
269    /// use rocket::config::TlsConfig;
270    ///
271    /// let tls_config = TlsConfig::from_paths("/ssl/certs.pem", "/ssl/key.pem");
272    /// ```
273    pub fn from_paths<C, K>(certs: C, key: K) -> Self
274        where C: AsRef<std::path::Path>, K: AsRef<std::path::Path>
275    {
276        TlsConfig {
277            certs: Either::Left(certs.as_ref().to_path_buf().into()),
278            key: Either::Left(key.as_ref().to_path_buf().into()),
279            ..TlsConfig::default()
280        }
281    }
282
283    /// Constructs a `TlsConfig` from byte buffers to a `certs`
284    /// certificate chain a `key` private-key. This method does no validation;
285    /// it simply creates a structure suitable for passing into a
286    /// [`Config`](crate::Config).
287    ///
288    /// # Example
289    ///
290    /// ```rust
291    /// use rocket::config::TlsConfig;
292    ///
293    /// # let certs_buf = &[];
294    /// # let key_buf = &[];
295    /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf);
296    /// ```
297    pub fn from_bytes(certs: &[u8], key: &[u8]) -> Self {
298        TlsConfig {
299            certs: Either::Right(certs.to_vec()),
300            key: Either::Right(key.to_vec()),
301            ..TlsConfig::default()
302        }
303    }
304
305    /// Sets the cipher suites supported by the server and their order of
306    /// preference from most to least preferred.
307    ///
308    /// If a suite appears more than once in `ciphers`, only the first suite
309    /// (and its relative order) is considered. If all cipher suites for a
310    /// version oF TLS are disabled, the respective protocol itself is disabled
311    /// entirely.
312    ///
313    /// # Example
314    ///
315    /// Disable TLS v1.2 by selecting only TLS v1.3 cipher suites:
316    ///
317    /// ```rust
318    /// use rocket::config::{TlsConfig, CipherSuite};
319    ///
320    /// # let certs_buf = &[];
321    /// # let key_buf = &[];
322    /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf)
323    ///     .with_ciphers(CipherSuite::TLS_V13_SET);
324    /// ```
325    ///
326    /// Enable only ChaCha20-Poly1305 based TLS v1.2 and TLS v1.3 cipher suites:
327    ///
328    /// ```rust
329    /// use rocket::config::{TlsConfig, CipherSuite};
330    ///
331    /// # let certs_buf = &[];
332    /// # let key_buf = &[];
333    /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf)
334    ///     .with_ciphers([
335    ///         CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
336    ///         CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
337    ///         CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
338    ///     ]);
339    /// ```
340    ///
341    /// Later duplicates are ignored.
342    ///
343    /// ```rust
344    /// use rocket::config::{TlsConfig, CipherSuite};
345    ///
346    /// # let certs_buf = &[];
347    /// # let key_buf = &[];
348    /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf)
349    ///     .with_ciphers([
350    ///         CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
351    ///         CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
352    ///         CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
353    ///         CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
354    ///         CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
355    ///     ]);
356    ///
357    /// let ciphers: Vec<_> = tls_config.ciphers().collect();
358    /// assert_eq!(ciphers, &[
359    ///     CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
360    ///     CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
361    ///     CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
362    /// ]);
363    /// ```
364    pub fn with_ciphers<I>(mut self, ciphers: I) -> Self
365        where I: IntoIterator<Item = CipherSuite>
366    {
367        self.ciphers = ciphers.into_iter().collect();
368        self
369    }
370
371    /// Whether to prefer the server's cipher suite order and ignore the
372    /// client's preferences (`true`) or choose the first supported ciphersuite
373    /// in the client's preference list (`false`). The default prefer's the
374    /// client's order (`false`).
375    ///
376    /// During TLS cipher suite negotiation, the client presents a set of
377    /// supported ciphers in its preferred order. From this list, the server
378    /// chooses one cipher suite. By default, the server chooses the first
379    /// cipher it supports from the list.
380    ///
381    /// By setting `prefer_server_order` to `true`, the server instead chooses
382    /// the first ciphersuite in it prefers that the client also supports,
383    /// ignoring the client's preferences.
384    ///
385    /// # Example
386    ///
387    /// ```rust
388    /// use rocket::config::{TlsConfig, CipherSuite};
389    ///
390    /// # let certs_buf = &[];
391    /// # let key_buf = &[];
392    /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf)
393    ///     .with_ciphers(CipherSuite::TLS_V13_SET)
394    ///     .with_preferred_server_cipher_order(true);
395    /// ```
396    pub fn with_preferred_server_cipher_order(mut self, prefer_server_order: bool) -> Self {
397        self.prefer_server_cipher_order = prefer_server_order;
398        self
399    }
400
401    /// Configures mutual TLS. See [`MutualTls`] for details.
402    ///
403    /// # Example
404    ///
405    /// ```rust
406    /// use rocket::config::{TlsConfig, MutualTls};
407    ///
408    /// # let certs = &[];
409    /// # let key = &[];
410    /// let mtls_config = MutualTls::from_path("path/to/cert.pem").mandatory(true);
411    /// let tls_config = TlsConfig::from_bytes(certs, key).with_mutual(mtls_config);
412    /// assert!(tls_config.mutual().is_some());
413    /// ```
414    #[cfg(feature = "mtls")]
415    #[cfg_attr(nightly, doc(cfg(feature = "mtls")))]
416    pub fn with_mutual(mut self, config: MutualTls) -> Self {
417        self.mutual = Some(config);
418        self
419    }
420
421    /// Returns the value of the `certs` parameter.
422    ///
423    /// # Example
424    ///
425    /// ```rust
426    /// use rocket::Config;
427    ///
428    /// let figment = Config::figment()
429    ///     .merge(("tls.certs", vec![0; 32]))
430    ///     .merge(("tls.key", "/etc/ssl/key.pem"));
431    ///
432    /// let config = rocket::Config::from(figment);
433    /// let tls_config = config.tls.as_ref().unwrap();
434    /// let cert_bytes = tls_config.certs().right().unwrap();
435    /// assert!(cert_bytes.iter().all(|&b| b == 0));
436    /// ```
437    pub fn certs(&self) -> either::Either<std::path::PathBuf, &[u8]> {
438        match &self.certs {
439            Either::Left(path) => either::Either::Left(path.relative()),
440            Either::Right(bytes) => either::Either::Right(&bytes),
441        }
442    }
443
444    /// Returns the value of the `key` parameter.
445    ///
446    /// # Example
447    ///
448    /// ```rust
449    /// use std::path::Path;
450    /// use rocket::Config;
451    ///
452    /// let figment = Config::figment()
453    ///     .merge(("tls.certs", vec![0; 32]))
454    ///     .merge(("tls.key", "/etc/ssl/key.pem"));
455    ///
456    /// let config = rocket::Config::from(figment);
457    /// let tls_config = config.tls.as_ref().unwrap();
458    /// let key_path = tls_config.key().left().unwrap();
459    /// assert_eq!(key_path, Path::new("/etc/ssl/key.pem"));
460    /// ```
461    pub fn key(&self) -> either::Either<std::path::PathBuf, &[u8]> {
462        match &self.key {
463            Either::Left(path) => either::Either::Left(path.relative()),
464            Either::Right(bytes) => either::Either::Right(&bytes),
465        }
466    }
467
468    /// Returns an iterator over the enabled cipher suites in their order of
469    /// preference from most to least preferred.
470    ///
471    /// # Example
472    ///
473    /// ```rust
474    /// use rocket::config::{TlsConfig, CipherSuite};
475    ///
476    /// # let certs_buf = &[];
477    /// # let key_buf = &[];
478    /// // The default set is CipherSuite::DEFAULT_SET.
479    /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf);
480    /// assert_eq!(tls_config.ciphers().count(), 9);
481    /// assert!(tls_config.ciphers().eq(CipherSuite::DEFAULT_SET.iter().copied()));
482    ///
483    /// // Enable only the TLS v1.3 ciphers.
484    /// let tls_v13_config = TlsConfig::from_bytes(certs_buf, key_buf)
485    ///     .with_ciphers(CipherSuite::TLS_V13_SET);
486    ///
487    /// assert_eq!(tls_v13_config.ciphers().count(), 3);
488    /// assert!(tls_v13_config.ciphers().eq(CipherSuite::TLS_V13_SET.iter().copied()));
489    /// ```
490    pub fn ciphers(&self) -> impl Iterator<Item = CipherSuite> + '_ {
491        self.ciphers.iter().copied()
492    }
493
494    /// Whether the server's cipher suite ordering is preferred or not.
495    ///
496    /// # Example
497    ///
498    /// ```rust
499    /// use rocket::config::TlsConfig;
500    ///
501    /// # let certs_buf = &[];
502    /// # let key_buf = &[];
503    /// // The default prefers the server's order.
504    /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf);
505    /// assert!(!tls_config.prefer_server_cipher_order());
506    ///
507    /// // Which can be overridden with the eponymous builder method.
508    /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf)
509    ///     .with_preferred_server_cipher_order(true);
510    ///
511    /// assert!(tls_config.prefer_server_cipher_order());
512    /// ```
513    pub fn prefer_server_cipher_order(&self) -> bool {
514        self.prefer_server_cipher_order
515    }
516
517    /// Returns the value of the `mutual` parameter.
518    ///
519    /// # Example
520    ///
521    /// ```rust
522    /// use std::path::Path;
523    /// use rocket::config::{TlsConfig, MutualTls};
524    ///
525    /// # let certs = &[];
526    /// # let key = &[];
527    /// let mtls_config = MutualTls::from_path("path/to/cert.pem").mandatory(true);
528    /// let tls_config = TlsConfig::from_bytes(certs, key).with_mutual(mtls_config);
529    ///
530    /// let mtls = tls_config.mutual().unwrap();
531    /// assert_eq!(mtls.ca_certs().unwrap_left(), Path::new("path/to/cert.pem"));
532    /// assert!(mtls.mandatory);
533    /// ```
534    #[cfg(feature = "mtls")]
535    #[cfg_attr(nightly, doc(cfg(feature = "mtls")))]
536    pub fn mutual(&self) -> Option<&MutualTls> {
537        self.mutual.as_ref()
538    }
539}
540
541#[cfg(feature = "mtls")]
542impl MutualTls {
543    /// Constructs a `MutualTls` from a path to a PEM file with a certificate
544    /// authority `ca_certs` DER-encoded X.509 TLS certificate chain. This
545    /// method does no validation; it simply creates a structure suitable for
546    /// passing into a [`TlsConfig`].
547    ///
548    /// These certificates will be used to verify client-presented certificates
549    /// in TLS connections.
550    ///
551    /// # Example
552    ///
553    /// ```rust
554    /// use rocket::config::MutualTls;
555    ///
556    /// let tls_config = MutualTls::from_path("/ssl/ca_certs.pem");
557    /// ```
558    pub fn from_path<C: AsRef<std::path::Path>>(ca_certs: C) -> Self {
559        MutualTls {
560            ca_certs: Either::Left(ca_certs.as_ref().to_path_buf().into()),
561            mandatory: Default::default()
562        }
563    }
564
565    /// Constructs a `MutualTls` from a byte buffer to a certificate authority
566    /// `ca_certs` DER-encoded X.509 TLS certificate chain. This method does no
567    /// validation; it simply creates a structure suitable for passing into a
568    /// [`TlsConfig`].
569    ///
570    /// These certificates will be used to verify client-presented certificates
571    /// in TLS connections.
572    ///
573    /// # Example
574    ///
575    /// ```rust
576    /// use rocket::config::MutualTls;
577    ///
578    /// # let ca_certs_buf = &[];
579    /// let mtls_config = MutualTls::from_bytes(ca_certs_buf);
580    /// ```
581    pub fn from_bytes(ca_certs: &[u8]) -> Self {
582        MutualTls {
583            ca_certs: Either::Right(ca_certs.to_vec()),
584            mandatory: Default::default()
585        }
586    }
587
588    /// Sets whether client authentication is required. Disabled by default.
589    ///
590    /// When `true`, client authentication will be required. TLS connections
591    /// where the client does not present a certificate will be immediately
592    /// terminated. When `false`, the client is not required to present a
593    /// certificate. In either case, if a certificate _is_ presented, it must be
594    /// valid or the connection is terminated.
595    ///
596    /// # Example
597    ///
598    /// ```rust
599    /// use rocket::config::MutualTls;
600    ///
601    /// # let ca_certs_buf = &[];
602    /// let mtls_config = MutualTls::from_bytes(ca_certs_buf).mandatory(true);
603    /// ```
604    pub fn mandatory(mut self, mandatory: bool) -> Self {
605        self.mandatory = mandatory;
606        self
607    }
608
609    /// Returns the value of the `ca_certs` parameter.
610    /// # Example
611    ///
612    /// ```rust
613    /// use rocket::config::MutualTls;
614    ///
615    /// # let ca_certs_buf = &[];
616    /// let mtls_config = MutualTls::from_bytes(ca_certs_buf).mandatory(true);
617    /// assert_eq!(mtls_config.ca_certs().unwrap_right(), ca_certs_buf);
618    /// ```
619    pub fn ca_certs(&self) -> either::Either<std::path::PathBuf, &[u8]> {
620        match &self.ca_certs {
621            Either::Left(path) => either::Either::Left(path.relative()),
622            Either::Right(bytes) => either::Either::Right(&bytes),
623        }
624    }
625}
626
627#[cfg(feature = "tls")]
628mod with_tls_feature {
629    use std::fs;
630    use std::io::{self, Error};
631
632    use crate::http::tls::Config;
633    use crate::http::tls::rustls::SupportedCipherSuite as RustlsCipher;
634    use crate::http::tls::rustls::cipher_suite;
635
636    use yansi::Paint;
637
638    use super::{Either, RelativePathBuf, TlsConfig, CipherSuite};
639
640    type Reader = Box<dyn std::io::BufRead + Sync + Send>;
641
642    fn to_reader(value: &Either<RelativePathBuf, Vec<u8>>) -> io::Result<Reader> {
643        match value {
644            Either::Left(path) => {
645                let path = path.relative();
646                let file = fs::File::open(&path).map_err(move |e| {
647                    let source = figment::Source::File(path);
648                    let msg = format!("error reading TLS file `{}`: {}", source.primary(), e);
649                    Error::new(e.kind(), msg)
650                })?;
651
652                Ok(Box::new(io::BufReader::new(file)))
653            }
654            Either::Right(vec) => Ok(Box::new(io::Cursor::new(vec.clone()))),
655        }
656    }
657
658    impl TlsConfig {
659        /// This is only called when TLS is enabled.
660        pub(crate) fn to_native_config(&self) -> io::Result<Config<Reader>> {
661            Ok(Config {
662                cert_chain: to_reader(&self.certs)?,
663                private_key: to_reader(&self.key)?,
664                ciphersuites: self.rustls_ciphers().collect(),
665                prefer_server_order: self.prefer_server_cipher_order,
666                #[cfg(not(feature = "mtls"))]
667                mandatory_mtls: false,
668                #[cfg(not(feature = "mtls"))]
669                ca_certs: None,
670                #[cfg(feature = "mtls")]
671                mandatory_mtls: self.mutual.as_ref().map_or(false, |m| m.mandatory),
672                #[cfg(feature = "mtls")]
673                ca_certs: match self.mutual {
674                    Some(ref mtls) => Some(to_reader(&mtls.ca_certs)?),
675                    None => None
676                },
677            })
678        }
679
680        fn rustls_ciphers(&self) -> impl Iterator<Item = RustlsCipher> + '_ {
681            self.ciphers().map(|ciphersuite| match ciphersuite {
682                CipherSuite::TLS_CHACHA20_POLY1305_SHA256 =>
683                    cipher_suite::TLS13_CHACHA20_POLY1305_SHA256,
684                CipherSuite::TLS_AES_256_GCM_SHA384 =>
685                    cipher_suite::TLS13_AES_256_GCM_SHA384,
686                CipherSuite::TLS_AES_128_GCM_SHA256 =>
687                    cipher_suite::TLS13_AES_128_GCM_SHA256,
688                CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 =>
689                    cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
690                CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 =>
691                    cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
692                CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 =>
693                    cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
694                CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 =>
695                    cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
696                CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 =>
697                    cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
698                CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 =>
699                    cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
700            })
701        }
702    }
703}