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}