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}