rocket_community/tls/
config.rs

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