268 lines
9.2 KiB
Rust
268 lines
9.2 KiB
Rust
use crate::error::Error;
|
|
use crate::kx::{SupportedKxGroup, ALL_KX_GROUPS};
|
|
use crate::suites::{SupportedCipherSuite, DEFAULT_CIPHER_SUITES};
|
|
use crate::versions;
|
|
|
|
use std::fmt;
|
|
use std::marker::PhantomData;
|
|
|
|
/// Building a [`ServerConfig`] or [`ClientConfig`] in a linker-friendly and
|
|
/// complete way.
|
|
///
|
|
/// Linker-friendly: meaning unused cipher suites, protocol
|
|
/// versions, key exchange mechanisms, etc. can be discarded
|
|
/// by the linker as they'll be unreferenced.
|
|
///
|
|
/// Complete: the type system ensures all decisions required to run a
|
|
/// server or client have been made by the time the process finishes.
|
|
///
|
|
/// Example, to make a [`ServerConfig`]:
|
|
///
|
|
/// ```no_run
|
|
/// # use rustls::ServerConfig;
|
|
/// # let certs = vec![];
|
|
/// # let private_key = rustls::PrivateKey(vec![]);
|
|
/// ServerConfig::builder()
|
|
/// .with_safe_default_cipher_suites()
|
|
/// .with_safe_default_kx_groups()
|
|
/// .with_safe_default_protocol_versions()
|
|
/// .unwrap()
|
|
/// .with_no_client_auth()
|
|
/// .with_single_cert(certs, private_key)
|
|
/// .expect("bad certificate/key");
|
|
/// ```
|
|
///
|
|
/// This may be shortened to:
|
|
///
|
|
/// ```no_run
|
|
/// # use rustls::ServerConfig;
|
|
/// # let certs = vec![];
|
|
/// # let private_key = rustls::PrivateKey(vec![]);
|
|
/// ServerConfig::builder()
|
|
/// .with_safe_defaults()
|
|
/// .with_no_client_auth()
|
|
/// .with_single_cert(certs, private_key)
|
|
/// .expect("bad certificate/key");
|
|
/// ```
|
|
///
|
|
/// To make a [`ClientConfig`]:
|
|
///
|
|
/// ```no_run
|
|
/// # use rustls::ClientConfig;
|
|
/// # let root_certs = rustls::RootCertStore::empty();
|
|
/// # let certs = vec![];
|
|
/// # let private_key = rustls::PrivateKey(vec![]);
|
|
/// ClientConfig::builder()
|
|
/// .with_safe_default_cipher_suites()
|
|
/// .with_safe_default_kx_groups()
|
|
/// .with_safe_default_protocol_versions()
|
|
/// .unwrap()
|
|
/// .with_root_certificates(root_certs)
|
|
/// .with_single_cert(certs, private_key)
|
|
/// .expect("bad certificate/key");
|
|
/// ```
|
|
///
|
|
/// This may be shortened to:
|
|
///
|
|
/// ```
|
|
/// # use rustls::ClientConfig;
|
|
/// # let root_certs = rustls::RootCertStore::empty();
|
|
/// ClientConfig::builder()
|
|
/// .with_safe_defaults()
|
|
/// .with_root_certificates(root_certs)
|
|
/// .with_no_client_auth();
|
|
/// ```
|
|
///
|
|
/// The types used here fit together like this:
|
|
///
|
|
/// 1. Call [`ClientConfig::builder()`] or [`ServerConfig::builder()`] to initialize a builder.
|
|
/// 1. You must make a decision on which cipher suites to use, typically
|
|
/// by calling [`ConfigBuilder<S, WantsCipherSuites>::with_safe_default_cipher_suites()`].
|
|
/// 2. Now you must make a decision
|
|
/// on key exchange groups: typically by calling
|
|
/// [`ConfigBuilder<S, WantsKxGroups>::with_safe_default_kx_groups()`].
|
|
/// 3. Now you must make
|
|
/// a decision on which protocol versions to support, typically by calling
|
|
/// [`ConfigBuilder<S, WantsVersions>::with_safe_default_protocol_versions()`].
|
|
/// 5. Now see [`ConfigBuilder<ClientConfig, WantsVerifier>`] or
|
|
/// [`ConfigBuilder<ServerConfig, WantsVerifier>`] for further steps.
|
|
///
|
|
/// [`ServerConfig`]: crate::ServerConfig
|
|
/// [`ClientConfig`]: crate::ClientConfig
|
|
/// [`ClientConfig::builder()`]: crate::ClientConfig::builder()
|
|
/// [`ServerConfig::builder()`]: crate::ServerConfig::builder()
|
|
/// [`ConfigBuilder<ClientConfig, WantsVerifier>`]: struct.ConfigBuilder.html#impl-3
|
|
/// [`ConfigBuilder<ServerConfig, WantsVerifier>`]: struct.ConfigBuilder.html#impl-6
|
|
#[derive(Clone)]
|
|
pub struct ConfigBuilder<Side: ConfigSide, State> {
|
|
pub(crate) state: State,
|
|
pub(crate) side: PhantomData<Side>,
|
|
}
|
|
|
|
impl<Side: ConfigSide, State: fmt::Debug> fmt::Debug for ConfigBuilder<Side, State> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let side_name = std::any::type_name::<Side>();
|
|
let side_name = side_name
|
|
.split("::")
|
|
.last()
|
|
.unwrap_or(side_name);
|
|
f.debug_struct(&format!("ConfigBuilder<{}, _>", side_name))
|
|
.field("state", &self.state)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
/// Config builder state where the caller must supply cipher suites.
|
|
///
|
|
/// For more information, see the [`ConfigBuilder`] documentation.
|
|
#[derive(Clone, Debug)]
|
|
pub struct WantsCipherSuites(pub(crate) ());
|
|
|
|
impl<S: ConfigSide> ConfigBuilder<S, WantsCipherSuites> {
|
|
/// Start side-specific config with defaults for underlying cryptography.
|
|
///
|
|
/// If used, this will enable all safe supported cipher suites ([`DEFAULT_CIPHER_SUITES`]), all
|
|
/// safe supported key exchange groups ([`ALL_KX_GROUPS`]) and all safe supported protocol
|
|
/// versions ([`DEFAULT_VERSIONS`]).
|
|
///
|
|
/// These are safe defaults, useful for 99% of applications.
|
|
///
|
|
/// [`DEFAULT_VERSIONS`]: versions::DEFAULT_VERSIONS
|
|
pub fn with_safe_defaults(self) -> ConfigBuilder<S, WantsVerifier> {
|
|
ConfigBuilder {
|
|
state: WantsVerifier {
|
|
cipher_suites: DEFAULT_CIPHER_SUITES.to_vec(),
|
|
kx_groups: ALL_KX_GROUPS.to_vec(),
|
|
versions: versions::EnabledVersions::new(versions::DEFAULT_VERSIONS),
|
|
},
|
|
side: self.side,
|
|
}
|
|
}
|
|
|
|
/// Choose a specific set of cipher suites.
|
|
pub fn with_cipher_suites(
|
|
self,
|
|
cipher_suites: &[SupportedCipherSuite],
|
|
) -> ConfigBuilder<S, WantsKxGroups> {
|
|
ConfigBuilder {
|
|
state: WantsKxGroups {
|
|
cipher_suites: cipher_suites.to_vec(),
|
|
},
|
|
side: self.side,
|
|
}
|
|
}
|
|
|
|
/// Choose the default set of cipher suites ([`DEFAULT_CIPHER_SUITES`]).
|
|
///
|
|
/// Note that this default provides only high-quality suites: there is no need
|
|
/// to filter out low-, export- or NULL-strength cipher suites: rustls does not
|
|
/// implement these.
|
|
pub fn with_safe_default_cipher_suites(self) -> ConfigBuilder<S, WantsKxGroups> {
|
|
self.with_cipher_suites(DEFAULT_CIPHER_SUITES)
|
|
}
|
|
}
|
|
|
|
/// Config builder state where the caller must supply key exchange groups.
|
|
///
|
|
/// For more information, see the [`ConfigBuilder`] documentation.
|
|
#[derive(Clone, Debug)]
|
|
pub struct WantsKxGroups {
|
|
cipher_suites: Vec<SupportedCipherSuite>,
|
|
}
|
|
|
|
impl<S: ConfigSide> ConfigBuilder<S, WantsKxGroups> {
|
|
/// Choose a specific set of key exchange groups.
|
|
pub fn with_kx_groups(
|
|
self,
|
|
kx_groups: &[&'static SupportedKxGroup],
|
|
) -> ConfigBuilder<S, WantsVersions> {
|
|
ConfigBuilder {
|
|
state: WantsVersions {
|
|
cipher_suites: self.state.cipher_suites,
|
|
kx_groups: kx_groups.to_vec(),
|
|
},
|
|
side: self.side,
|
|
}
|
|
}
|
|
|
|
/// Choose the default set of key exchange groups ([`ALL_KX_GROUPS`]).
|
|
///
|
|
/// This is a safe default: rustls doesn't implement any poor-quality groups.
|
|
pub fn with_safe_default_kx_groups(self) -> ConfigBuilder<S, WantsVersions> {
|
|
self.with_kx_groups(&ALL_KX_GROUPS)
|
|
}
|
|
}
|
|
|
|
/// Config builder state where the caller must supply TLS protocol versions.
|
|
///
|
|
/// For more information, see the [`ConfigBuilder`] documentation.
|
|
#[derive(Clone, Debug)]
|
|
pub struct WantsVersions {
|
|
cipher_suites: Vec<SupportedCipherSuite>,
|
|
kx_groups: Vec<&'static SupportedKxGroup>,
|
|
}
|
|
|
|
impl<S: ConfigSide> ConfigBuilder<S, WantsVersions> {
|
|
/// Accept the default protocol versions: both TLS1.2 and TLS1.3 are enabled.
|
|
pub fn with_safe_default_protocol_versions(
|
|
self,
|
|
) -> Result<ConfigBuilder<S, WantsVerifier>, Error> {
|
|
self.with_protocol_versions(versions::DEFAULT_VERSIONS)
|
|
}
|
|
|
|
/// Use a specific set of protocol versions.
|
|
pub fn with_protocol_versions(
|
|
self,
|
|
versions: &[&'static versions::SupportedProtocolVersion],
|
|
) -> Result<ConfigBuilder<S, WantsVerifier>, Error> {
|
|
let mut any_usable_suite = false;
|
|
for suite in &self.state.cipher_suites {
|
|
if versions.contains(&suite.version()) {
|
|
any_usable_suite = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if !any_usable_suite {
|
|
return Err(Error::General("no usable cipher suites configured".into()));
|
|
}
|
|
|
|
if self.state.kx_groups.is_empty() {
|
|
return Err(Error::General("no kx groups configured".into()));
|
|
}
|
|
|
|
Ok(ConfigBuilder {
|
|
state: WantsVerifier {
|
|
cipher_suites: self.state.cipher_suites,
|
|
kx_groups: self.state.kx_groups,
|
|
versions: versions::EnabledVersions::new(versions),
|
|
},
|
|
side: self.side,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Config builder state where the caller must supply a verifier.
|
|
///
|
|
/// For more information, see the [`ConfigBuilder`] documentation.
|
|
#[derive(Clone, Debug)]
|
|
pub struct WantsVerifier {
|
|
pub(crate) cipher_suites: Vec<SupportedCipherSuite>,
|
|
pub(crate) kx_groups: Vec<&'static SupportedKxGroup>,
|
|
pub(crate) versions: versions::EnabledVersions,
|
|
}
|
|
|
|
/// Helper trait to abstract [`ConfigBuilder`] over building a [`ClientConfig`] or [`ServerConfig`].
|
|
///
|
|
/// [`ClientConfig`]: crate::ClientConfig
|
|
/// [`ServerConfig`]: crate::ServerConfig
|
|
pub trait ConfigSide: sealed::Sealed {}
|
|
|
|
impl ConfigSide for crate::ClientConfig {}
|
|
impl ConfigSide for crate::ServerConfig {}
|
|
|
|
mod sealed {
|
|
pub trait Sealed {}
|
|
impl Sealed for crate::ClientConfig {}
|
|
impl Sealed for crate::ServerConfig {}
|
|
}
|