rocket/tls/
config.rs

1use std::io;
2use std::sync::Arc;
3
4use futures::TryFutureExt;
5use figment::value::magic::{Either, RelativePathBuf};
6use serde::{Deserialize, Serialize};
7use indexmap::IndexSet;
8use rustls::crypto::{ring, CryptoProvider};
9use rustls::pki_types::{CertificateDer, PrivateKeyDer};
10use rustls::server::{ServerSessionMemoryCache, ServerConfig, WebPkiClientVerifier};
11
12use crate::tls::resolver::DynResolver;
13use crate::tls::error::{Result, Error, KeyError};
14
15/// TLS configuration: certificate chain, key, and ciphersuites.
16///
17/// Four parameters control `tls` configuration:
18///
19///   * `certs`, `key`
20///
21///     Both `certs` and `key` can be configured as a path or as raw bytes.
22///     `certs` must be a DER-encoded X.509 TLS certificate chain, while `key`
23///     must be a DER-encoded ASN.1 key in either PKCS#8, PKCS#1, or SEC1
24///     format. When a path is configured in a file, such as `Rocket.toml`,
25///     relative paths are interpreted as relative to the source file's
26///     directory.
27///
28///   * `ciphers`
29///
30///     A list of supported [`CipherSuite`]s in server-preferred order, from
31///     most to least. It is not required and defaults to
32///     [`CipherSuite::DEFAULT_SET`], the recommended setting.
33///
34///   * `prefer_server_cipher_order`
35///
36///     A boolean that indicates whether the server should regard its own
37///     ciphersuite preferences over the client's. The default and recommended
38///     value is `false`.
39///
40/// Additionally, the `mutual` parameter controls if and how the server
41/// authenticates clients via mutual TLS. It works in concert with the
42/// [`mtls`](crate::mtls) module. See [`MtlsConfig`](crate::mtls::MtlsConfig)
43/// for configuration details.
44///
45/// In `Rocket.toml`, configuration might look like:
46///
47/// ```toml
48/// [default.tls]
49/// certs = "private/rsa_sha256_cert.pem"
50/// key = "private/rsa_sha256_key.pem"
51/// ```
52///
53/// With a custom programmatic configuration, this might look like:
54///
55/// ```rust
56/// # #[macro_use] extern crate rocket;
57/// use rocket::tls::{TlsConfig, CipherSuite};
58/// use rocket::figment::providers::Serialized;
59///
60/// #[launch]
61/// fn rocket() -> _ {
62///     let tls = TlsConfig::from_paths("/ssl/certs.pem", "/ssl/key.pem")
63///         .with_ciphers(CipherSuite::TLS_V13_SET)
64///         .with_preferred_server_cipher_order(true);
65///
66///     rocket::custom(rocket::Config::figment().merge(("tls", tls)))
67/// }
68/// ```
69///
70/// Or by creating a custom figment:
71///
72/// ```rust
73/// use rocket::figment::Figment;
74/// use rocket::tls::TlsConfig;
75///
76/// let figment = Figment::new()
77///     .merge(("certs", "path/to/certs.pem"))
78///     .merge(("key", vec![0; 32]));
79/// #
80/// # let tls_config: TlsConfig = figment.extract().unwrap();
81/// # assert!(tls_config.certs().is_left());
82/// # assert!(tls_config.key().is_right());
83/// # assert_eq!(tls_config.ciphers().count(), 9);
84/// # assert!(!tls_config.prefer_server_cipher_order());
85/// ```
86#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
87pub struct TlsConfig {
88    /// Path to a PEM file with, or raw bytes for, a DER-encoded X.509 TLS
89    /// certificate chain.
90    pub(crate) certs: Either<RelativePathBuf, Vec<u8>>,
91    /// Path to a PEM file with, or raw bytes for, DER-encoded private key in
92    /// either PKCS#8 or PKCS#1 format.
93    pub(crate) key: Either<RelativePathBuf, Vec<u8>>,
94    /// List of TLS cipher suites in server-preferred order.
95    #[serde(default = "CipherSuite::default_set")]
96    pub(crate) ciphers: IndexSet<CipherSuite>,
97    /// Whether to prefer the server's cipher suite order over the client's.
98    #[serde(default)]
99    pub(crate) prefer_server_cipher_order: bool,
100    /// Configuration for mutual TLS, if any.
101    #[serde(default)]
102    #[cfg(feature = "mtls")]
103    #[cfg_attr(nightly, doc(cfg(feature = "mtls")))]
104    pub(crate) mutual: Option<crate::mtls::MtlsConfig>,
105    #[serde(skip)]
106    pub(crate) resolver: Option<DynResolver>,
107}
108
109/// A supported TLS cipher suite.
110#[allow(non_camel_case_types)]
111#[derive(PartialEq, Eq, Debug, Copy, Clone, Hash, Deserialize, Serialize)]
112#[non_exhaustive]
113pub enum CipherSuite {
114    /// The TLS 1.3 `TLS_CHACHA20_POLY1305_SHA256` cipher suite.
115    TLS_CHACHA20_POLY1305_SHA256,
116    /// The TLS 1.3 `TLS_AES_256_GCM_SHA384` cipher suite.
117    TLS_AES_256_GCM_SHA384,
118    /// The TLS 1.3 `TLS_AES_128_GCM_SHA256` cipher suite.
119    TLS_AES_128_GCM_SHA256,
120
121    /// The TLS 1.2 `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256` cipher suite.
122    TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
123    /// The TLS 1.2 `TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256` cipher suite.
124    TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
125    /// The TLS 1.2 `TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384` cipher suite.
126    TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
127    /// The TLS 1.2 `TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256` cipher suite.
128    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
129    /// The TLS 1.2 `TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384` cipher suite.
130    TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
131    /// The TLS 1.2 `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256` cipher suite.
132    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
133}
134
135impl Default for TlsConfig {
136    fn default() -> Self {
137        TlsConfig {
138            certs: Either::Right(vec![]),
139            key: Either::Right(vec![]),
140            ciphers: CipherSuite::default_set(),
141            prefer_server_cipher_order: false,
142            #[cfg(feature = "mtls")]
143            mutual: None,
144            resolver: None,
145        }
146    }
147}
148
149impl TlsConfig {
150    /// Constructs a `TlsConfig` from paths to a `certs` certificate chain
151    /// a `key` private-key. This method does no validation; it simply creates a
152    /// structure suitable for passing into a [`Config`](crate::Config).
153    ///
154    /// # Example
155    ///
156    /// ```rust
157    /// use rocket::tls::TlsConfig;
158    ///
159    /// let tls_config = TlsConfig::from_paths("/ssl/certs.pem", "/ssl/key.pem");
160    /// ```
161    pub fn from_paths<C, K>(certs: C, key: K) -> Self
162        where C: AsRef<std::path::Path>, K: AsRef<std::path::Path>,
163    {
164        TlsConfig {
165            certs: Either::Left(certs.as_ref().to_path_buf().into()),
166            key: Either::Left(key.as_ref().to_path_buf().into()),
167            ..TlsConfig::default()
168        }
169    }
170
171    /// Constructs a `TlsConfig` from byte buffers to a `certs`
172    /// certificate chain a `key` private-key. This method does no validation;
173    /// it simply creates a structure suitable for passing into a
174    /// [`Config`](crate::Config).
175    ///
176    /// # Example
177    ///
178    /// ```rust
179    /// use rocket::tls::TlsConfig;
180    ///
181    /// # let certs_buf = &[];
182    /// # let key_buf = &[];
183    /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf);
184    /// ```
185    pub fn from_bytes(certs: &[u8], key: &[u8]) -> Self {
186        TlsConfig {
187            certs: Either::Right(certs.to_vec()),
188            key: Either::Right(key.to_vec()),
189            ..TlsConfig::default()
190        }
191    }
192
193    /// Sets the cipher suites supported by the server and their order of
194    /// preference from most to least preferred.
195    ///
196    /// If a suite appears more than once in `ciphers`, only the first suite
197    /// (and its relative order) is considered. If all cipher suites for a
198    /// version oF TLS are disabled, the respective protocol itself is disabled
199    /// entirely.
200    ///
201    /// # Example
202    ///
203    /// Disable TLS v1.2 by selecting only TLS v1.3 cipher suites:
204    ///
205    /// ```rust
206    /// use rocket::tls::{TlsConfig, CipherSuite};
207    ///
208    /// # let certs_buf = &[];
209    /// # let key_buf = &[];
210    /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf)
211    ///     .with_ciphers(CipherSuite::TLS_V13_SET);
212    /// ```
213    ///
214    /// Enable only ChaCha20-Poly1305 based TLS v1.2 and TLS v1.3 cipher suites:
215    ///
216    /// ```rust
217    /// use rocket::tls::{TlsConfig, CipherSuite};
218    ///
219    /// # let certs_buf = &[];
220    /// # let key_buf = &[];
221    /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf)
222    ///     .with_ciphers([
223    ///         CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
224    ///         CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
225    ///         CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
226    ///     ]);
227    /// ```
228    ///
229    /// Later duplicates are ignored.
230    ///
231    /// ```rust
232    /// use rocket::tls::{TlsConfig, CipherSuite};
233    ///
234    /// # let certs_buf = &[];
235    /// # let key_buf = &[];
236    /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf)
237    ///     .with_ciphers([
238    ///         CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
239    ///         CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
240    ///         CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
241    ///         CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
242    ///         CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
243    ///     ]);
244    ///
245    /// let ciphers: Vec<_> = tls_config.ciphers().collect();
246    /// assert_eq!(ciphers, &[
247    ///     CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
248    ///     CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
249    ///     CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
250    /// ]);
251    /// ```
252    pub fn with_ciphers<C>(mut self, ciphers: C) -> Self
253        where C: IntoIterator<Item = CipherSuite>
254    {
255        self.ciphers = ciphers.into_iter().collect();
256        self
257    }
258
259    /// Whether to prefer the server's cipher suite order and ignore the
260    /// client's preferences (`true`) or choose the first supported ciphersuite
261    /// in the client's preference list (`false`). The default prefer's the
262    /// client's order (`false`).
263    ///
264    /// During TLS cipher suite negotiation, the client presents a set of
265    /// supported ciphers in its preferred order. From this list, the server
266    /// chooses one cipher suite. By default, the server chooses the first
267    /// cipher it supports from the list.
268    ///
269    /// By setting `prefer_server_order` to `true`, the server instead chooses
270    /// the first ciphersuite in it prefers that the client also supports,
271    /// ignoring the client's preferences.
272    ///
273    /// # Example
274    ///
275    /// ```rust
276    /// use rocket::tls::{TlsConfig, CipherSuite};
277    ///
278    /// # let certs_buf = &[];
279    /// # let key_buf = &[];
280    /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf)
281    ///     .with_ciphers(CipherSuite::TLS_V13_SET)
282    ///     .with_preferred_server_cipher_order(true);
283    /// ```
284    pub fn with_preferred_server_cipher_order(mut self, prefer_server_order: bool) -> Self {
285        self.prefer_server_cipher_order = prefer_server_order;
286        self
287    }
288
289    /// Set mutual TLS configuration. See
290    /// [`MtlsConfig`](crate::mtls::MtlsConfig) for details.
291    ///
292    /// # Example
293    ///
294    /// ```rust
295    /// use rocket::tls::TlsConfig;
296    /// use rocket::mtls::MtlsConfig;
297    ///
298    /// # let certs = &[];
299    /// # let key = &[];
300    /// let mtls_config = MtlsConfig::from_path("path/to/cert.pem").mandatory(true);
301    /// let tls_config = TlsConfig::from_bytes(certs, key).with_mutual(mtls_config);
302    /// assert!(tls_config.mutual().is_some());
303    /// ```
304    #[cfg(feature = "mtls")]
305    #[cfg_attr(nightly, doc(cfg(feature = "mtls")))]
306    pub fn with_mutual(mut self, config: crate::mtls::MtlsConfig) -> Self {
307        self.mutual = Some(config);
308        self
309    }
310
311    /// Returns the value of the `certs` parameter.
312    ///
313    /// # Example
314    ///
315    /// ```rust
316    /// # use std::path::Path;
317    /// use rocket::tls::TlsConfig;
318    /// use rocket::figment::Figment;
319    ///
320    /// let figment = Figment::new()
321    ///     .merge(("certs", "/path/to/certs.pem"))
322    ///     .merge(("key", vec![0; 32]));
323    ///
324    /// let tls_config: TlsConfig = figment.extract().unwrap();
325    /// let cert_path = tls_config.certs().left().unwrap();
326    /// assert_eq!(cert_path, Path::new("/path/to/certs.pem"));
327    /// ```
328    pub fn certs(&self) -> either::Either<std::path::PathBuf, &[u8]> {
329        match &self.certs {
330            Either::Left(path) => either::Either::Left(path.relative()),
331            Either::Right(bytes) => either::Either::Right(bytes),
332        }
333    }
334
335    pub fn certs_reader(&self) -> io::Result<Box<dyn io::BufRead + Sync + Send>> {
336        to_reader(&self.certs)
337    }
338
339    /// Returns the value of the `key` parameter.
340    ///
341    /// # Example
342    ///
343    /// ```rust
344    /// # use std::path::Path;
345    /// use rocket::tls::TlsConfig;
346    /// use rocket::figment::Figment;
347    ///
348    /// let figment = Figment::new()
349    ///     .merge(("certs", vec![0; 32]))
350    ///     .merge(("key", "/etc/ssl/key.pem"));
351    ///
352    /// let tls_config: TlsConfig = figment.extract().unwrap();
353    /// let key_path = tls_config.key().left().unwrap();
354    /// assert_eq!(key_path, Path::new("/etc/ssl/key.pem"));
355    /// ```
356    pub fn key(&self) -> either::Either<std::path::PathBuf, &[u8]> {
357        match &self.key {
358            Either::Left(path) => either::Either::Left(path.relative()),
359            Either::Right(bytes) => either::Either::Right(bytes),
360        }
361    }
362
363    pub fn key_reader(&self) -> io::Result<Box<dyn io::BufRead + Sync + Send>> {
364        to_reader(&self.key)
365    }
366
367    /// Returns an iterator over the enabled cipher suites in their order of
368    /// preference from most to least preferred.
369    ///
370    /// # Example
371    ///
372    /// ```rust
373    /// use rocket::tls::{TlsConfig, CipherSuite};
374    ///
375    /// # let certs_buf = &[];
376    /// # let key_buf = &[];
377    /// // The default set is CipherSuite::DEFAULT_SET.
378    /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf);
379    /// assert_eq!(tls_config.ciphers().count(), 9);
380    /// assert!(tls_config.ciphers().eq(CipherSuite::DEFAULT_SET.iter().copied()));
381    ///
382    /// // Enable only the TLS v1.3 ciphers.
383    /// let tls_v13_config = TlsConfig::from_bytes(certs_buf, key_buf)
384    ///     .with_ciphers(CipherSuite::TLS_V13_SET);
385    ///
386    /// assert_eq!(tls_v13_config.ciphers().count(), 3);
387    /// assert!(tls_v13_config.ciphers().eq(CipherSuite::TLS_V13_SET.iter().copied()));
388    /// ```
389    pub fn ciphers(&self) -> impl Iterator<Item = CipherSuite> + '_ {
390        self.ciphers.iter().copied()
391    }
392
393    /// Whether the server's cipher suite ordering is preferred or not.
394    ///
395    /// # Example
396    ///
397    /// ```rust
398    /// use rocket::tls::TlsConfig;
399    ///
400    /// # let certs_buf = &[];
401    /// # let key_buf = &[];
402    /// // The default prefers the server's order.
403    /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf);
404    /// assert!(!tls_config.prefer_server_cipher_order());
405    ///
406    /// // Which can be overridden with the eponymous builder method.
407    /// let tls_config = TlsConfig::from_bytes(certs_buf, key_buf)
408    ///     .with_preferred_server_cipher_order(true);
409    ///
410    /// assert!(tls_config.prefer_server_cipher_order());
411    /// ```
412    pub fn prefer_server_cipher_order(&self) -> bool {
413        self.prefer_server_cipher_order
414    }
415
416    /// Returns the value of the `mutual` parameter.
417    ///
418    /// # Example
419    ///
420    /// ```rust
421    /// use std::path::Path;
422    ///
423    /// use rocket::tls::TlsConfig;
424    /// use rocket::mtls::MtlsConfig;
425    ///
426    /// # let certs = &[];
427    /// # let key = &[];
428    /// let mtls_config = MtlsConfig::from_path("path/to/cert.pem").mandatory(true);
429    /// let tls_config = TlsConfig::from_bytes(certs, key).with_mutual(mtls_config);
430    ///
431    /// let mtls = tls_config.mutual().unwrap();
432    /// assert_eq!(mtls.ca_certs().unwrap_left(), Path::new("path/to/cert.pem"));
433    /// assert!(mtls.mandatory);
434    /// ```
435    #[cfg(feature = "mtls")]
436    #[cfg_attr(nightly, doc(cfg(feature = "mtls")))]
437    pub fn mutual(&self) -> Option<&crate::mtls::MtlsConfig> {
438        self.mutual.as_ref()
439    }
440
441    /// Try to convert `self` into a [rustls] [`ServerConfig`].
442    ///
443    /// [`ServerConfig`]: rustls::server::ServerConfig
444    pub async fn server_config(&self) -> Result<rustls::server::ServerConfig> {
445        let this = self.clone();
446        tokio::task::spawn_blocking(move || this._server_config())
447            .map_err(io::Error::other)
448            .await?
449    }
450
451    /// Try to convert `self` into a [rustls] [`ServerConfig`].
452    ///
453    /// [`ServerConfig`]: rustls::server::ServerConfig
454    pub(crate) fn _server_config(&self) -> Result<rustls::server::ServerConfig> {
455        let provider = Arc::new(self.default_crypto_provider());
456
457        #[cfg(feature = "mtls")]
458        let verifier = match self.mutual {
459            Some(ref mtls) => {
460                let ca = Arc::new(mtls.load_ca_certs()?);
461                let verifier = WebPkiClientVerifier::builder_with_provider(ca, provider.clone());
462                match mtls.mandatory {
463                    true => verifier.build()?,
464                    false => verifier.allow_unauthenticated().build()?,
465                }
466            },
467            None => WebPkiClientVerifier::no_client_auth(),
468        };
469
470        #[cfg(not(feature = "mtls"))]
471        let verifier = WebPkiClientVerifier::no_client_auth();
472
473        let mut tls_config = ServerConfig::builder_with_provider(provider)
474            .with_safe_default_protocol_versions()?
475            .with_client_cert_verifier(verifier)
476            .with_single_cert(self.load_certs()?, self.load_key()?)?;
477
478        tls_config.ignore_client_order = self.prefer_server_cipher_order;
479        tls_config.session_storage = ServerSessionMemoryCache::new(1024);
480        tls_config.ticketer = rustls::crypto::ring::Ticketer::new()?;
481        tls_config.alpn_protocols = vec![b"http/1.1".to_vec()];
482        if cfg!(feature = "http2") {
483            tls_config.alpn_protocols.insert(0, b"h2".to_vec());
484        }
485
486        Ok(tls_config)
487    }
488
489    /// NOTE: This is a blocking function.
490    pub fn validate(&self) -> Result<()> {
491        self._server_config().map(|_| ())
492    }
493}
494
495/// Loads certificates from `reader`.
496impl TlsConfig {
497    pub(crate) fn load_certs(&self) -> Result<Vec<CertificateDer<'static>>> {
498        rustls_pemfile::certs(&mut self.certs_reader()?)
499            .collect::<Result<_, _>>()
500            .map_err(Error::CertChain)
501    }
502
503    /// Load and decode the private key  from `reader`.
504    pub(crate) fn load_key(&self) -> Result<PrivateKeyDer<'static>> {
505        use rustls_pemfile::Item::*;
506
507        let mut keys = rustls_pemfile::read_all(&mut self.key_reader()?)
508            .map(|result| result.map_err(KeyError::Io)
509                .and_then(|item| match item {
510                    Pkcs1Key(key) => Ok(key.into()),
511                    Pkcs8Key(key) => Ok(key.into()),
512                    Sec1Key(key) => Ok(key.into()),
513                    _ => Err(KeyError::BadItem(item))
514                })
515            )
516            .collect::<Result<Vec<PrivateKeyDer<'static>>, _>>()?;
517
518        if keys.len() != 1 {
519            return Err(KeyError::BadKeyCount(keys.len()).into());
520        }
521
522        // Ensure we can use the key.
523        let key = keys.remove(0);
524        self.default_crypto_provider()
525            .key_provider
526            .load_private_key(key.clone_key())
527            .map_err(KeyError::Unsupported)?;
528
529        Ok(key)
530    }
531
532    pub(crate) fn default_crypto_provider(&self) -> CryptoProvider {
533        CryptoProvider::get_default()
534            .map(|arc| (**arc).clone())
535            .unwrap_or_else(|| rustls::crypto::CryptoProvider {
536                cipher_suites: self.ciphers().map(|cipher| match cipher {
537                    CipherSuite::TLS_CHACHA20_POLY1305_SHA256 =>
538                        ring::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256,
539                    CipherSuite::TLS_AES_256_GCM_SHA384 =>
540                        ring::cipher_suite::TLS13_AES_256_GCM_SHA384,
541                    CipherSuite::TLS_AES_128_GCM_SHA256 =>
542                        ring::cipher_suite::TLS13_AES_128_GCM_SHA256,
543                    CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 =>
544                        ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
545                    CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 =>
546                        ring::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
547                    CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 =>
548                        ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
549                    CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 =>
550                        ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
551                    CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 =>
552                        ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
553                    CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 =>
554                        ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
555                }).collect(),
556                ..ring::default_provider()
557            })
558    }
559}
560
561impl CipherSuite {
562    /// The default set and order of cipher suites. These are all of the
563    /// variants in [`CipherSuite`] in their declaration order.
564    pub const DEFAULT_SET: [CipherSuite; 9] = [
565        // TLS v1.3 suites...
566        CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
567        CipherSuite::TLS_AES_256_GCM_SHA384,
568        CipherSuite::TLS_AES_128_GCM_SHA256,
569
570        // TLS v1.2 suites...
571        CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
572        CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
573        CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
574        CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
575        CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
576        CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
577    ];
578
579    /// The default set and order of cipher suites. These are the TLS 1.3
580    /// variants in [`CipherSuite`] in their declaration order.
581    pub const TLS_V13_SET: [CipherSuite; 3] = [
582        CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
583        CipherSuite::TLS_AES_256_GCM_SHA384,
584        CipherSuite::TLS_AES_128_GCM_SHA256,
585    ];
586
587    /// The default set and order of cipher suites. These are the TLS 1.2
588    /// variants in [`CipherSuite`] in their declaration order.
589    pub const TLS_V12_SET: [CipherSuite; 6] = [
590        CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
591        CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
592        CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
593        CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
594        CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
595        CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
596    ];
597
598    /// Used as the `serde` default for `ciphers`.
599    fn default_set() -> IndexSet<Self> {
600        Self::DEFAULT_SET.iter().copied().collect()
601    }
602}
603
604pub(crate) fn to_reader(
605    value: &Either<RelativePathBuf, Vec<u8>>
606) -> io::Result<Box<dyn io::BufRead + Sync + Send>> {
607    match value {
608        Either::Left(path) => {
609            let path = path.relative();
610            let file = std::fs::File::open(&path)
611                .map_err(move |e| {
612                    let source = figment::Source::File(path);
613                    let msg = format!("error reading TLS file `{source}`: {e}");
614                    io::Error::new(e.kind(), msg)
615                })?;
616
617            Ok(Box::new(io::BufReader::new(file)))
618        }
619        Either::Right(vec) => Ok(Box::new(io::Cursor::new(vec.clone()))),
620    }
621}
622
623#[cfg(test)]
624mod tests {
625    use super::*;
626    use figment::{Figment, providers::{Toml, Format}};
627
628    #[test]
629    fn test_tls_config_from_file() {
630        use crate::tls::{TlsConfig, CipherSuite};
631        use pretty_assertions::assert_eq;
632
633        figment::Jail::expect_with(|jail| {
634            jail.create_file("Rocket.toml", r#"
635                [global]
636                shutdown.ctrlc = 0
637                ident = false
638
639                [global.tls]
640                certs = "/ssl/cert.pem"
641                key = "/ssl/key.pem"
642
643                [global.limits]
644                forms = "1mib"
645                json = "10mib"
646                stream = "50kib"
647            "#)?;
648
649            let config: TlsConfig = crate::Config::figment().extract_inner("tls")?;
650            assert_eq!(config, TlsConfig::from_paths("/ssl/cert.pem", "/ssl/key.pem"));
651
652            jail.create_file("Rocket.toml", r#"
653                [global.tls]
654                certs = "cert.pem"
655                key = "key.pem"
656            "#)?;
657
658            let config: TlsConfig = crate::Config::figment().extract_inner("tls")?;
659            assert_eq!(config, TlsConfig::from_paths(
660                jail.directory().join("cert.pem"),
661                jail.directory().join("key.pem")
662            ));
663
664            jail.create_file("TLS.toml", r#"
665                certs = "cert.pem"
666                key = "key.pem"
667                prefer_server_cipher_order = true
668                ciphers = [
669                    "TLS_CHACHA20_POLY1305_SHA256",
670                    "TLS_AES_256_GCM_SHA384",
671                    "TLS_AES_128_GCM_SHA256",
672                    "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
673                    "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
674                    "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
675                ]
676            "#)?;
677
678            let config: TlsConfig = Figment::from(Toml::file("TLS.toml")).extract()?;
679            let cert_path = jail.directory().join("cert.pem");
680            let key_path = jail.directory().join("key.pem");
681            assert_eq!(config, TlsConfig::from_paths(cert_path, key_path)
682                 .with_preferred_server_cipher_order(true)
683                 .with_ciphers([
684                     CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
685                     CipherSuite::TLS_AES_256_GCM_SHA384,
686                     CipherSuite::TLS_AES_128_GCM_SHA256,
687                     CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
688                     CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
689                     CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
690                 ]));
691
692            jail.create_file("Rocket.toml", r#"
693                [global]
694                shutdown.ctrlc = 0
695                ident = false
696
697                [global.tls]
698                certs = "/ssl/cert.pem"
699                key = "/ssl/key.pem"
700
701                [global.limits]
702                forms = "1mib"
703                json = "10mib"
704                stream = "50kib"
705            "#)?;
706
707            let config: TlsConfig = crate::Config::figment().extract_inner("tls")?;
708            assert_eq!(config, TlsConfig::from_paths("/ssl/cert.pem", "/ssl/key.pem"));
709
710            jail.create_file("Rocket.toml", r#"
711                [global.tls]
712                certs = "cert.pem"
713                key = "key.pem"
714            "#)?;
715
716            let config: TlsConfig = crate::Config::figment().extract_inner("tls")?;
717            assert_eq!(config, TlsConfig::from_paths(
718                jail.directory().join("cert.pem"),
719                jail.directory().join("key.pem")
720            ));
721
722            jail.create_file("Rocket.toml", r#"
723                [global.tls]
724                certs = "cert.pem"
725                key = "key.pem"
726                prefer_server_cipher_order = true
727                ciphers = [
728                    "TLS_CHACHA20_POLY1305_SHA256",
729                    "TLS_AES_256_GCM_SHA384",
730                    "TLS_AES_128_GCM_SHA256",
731                    "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
732                    "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
733                    "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
734                ]
735            "#)?;
736
737            let config: TlsConfig = crate::Config::figment().extract_inner("tls")?;
738            let cert_path = jail.directory().join("cert.pem");
739            let key_path = jail.directory().join("key.pem");
740            assert_eq!(config, TlsConfig::from_paths(cert_path, key_path)
741                 .with_preferred_server_cipher_order(true)
742                 .with_ciphers([
743                     CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
744                     CipherSuite::TLS_AES_256_GCM_SHA384,
745                     CipherSuite::TLS_AES_128_GCM_SHA256,
746                     CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
747                     CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
748                     CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
749                 ]));
750
751            Ok(())
752        });
753    }
754
755    macro_rules! tls_example_private_pem {
756        ($k:expr) => {
757            concat!(env!("CARGO_MANIFEST_DIR"), "/../../examples/tls/private/", $k)
758        }
759    }
760
761    #[test]
762    fn verify_load_private_keys_of_different_types() -> Result<()> {
763        let key_paths = [
764            tls_example_private_pem!("rsa_sha256_key.pem"),
765            tls_example_private_pem!("ecdsa_nistp256_sha256_key_pkcs8.pem"),
766            tls_example_private_pem!("ecdsa_nistp384_sha384_key_pkcs8.pem"),
767            tls_example_private_pem!("ed25519_key.pem"),
768        ];
769
770        for key in key_paths {
771            TlsConfig::from_paths("", key).load_key()?;
772        }
773
774        Ok(())
775    }
776
777    #[test]
778    fn verify_load_certs_of_different_types() -> Result<()> {
779        let cert_paths = [
780            tls_example_private_pem!("rsa_sha256_cert.pem"),
781            tls_example_private_pem!("ecdsa_nistp256_sha256_cert.pem"),
782            tls_example_private_pem!("ecdsa_nistp384_sha384_cert.pem"),
783            tls_example_private_pem!("ed25519_cert.pem"),
784        ];
785
786        for cert in cert_paths {
787            TlsConfig::from_paths(cert, "").load_certs()?;
788        }
789
790        Ok(())
791    }
792}