#![allow(missing_docs)]
mod connector;
mod extension;
mod impersonate;
mod settings;
use crate::{connect::HttpConnector, HttpVersionPref};
use boring::{
error::ErrorStack,
ssl::{SslConnector, SslMethod, SslOptions, SslVersion},
};
pub use connector::MaybeHttpsStream;
use connector::{HttpsConnector, HttpsLayer, HttpsLayerSettings};
pub use extension::cert_compression;
use extension::{TlsConnectExtension, TlsExtension};
pub use impersonate::{
chrome, edge, okhttp, safari, tls_settings, Impersonate, ImpersonateSettings,
};
pub use settings::{Http2Settings, TlsSettings};
type TlsResult<T> = std::result::Result<T, ErrorStack>;
type ConnectLayer = HttpsLayer;
#[derive(Clone)]
#[allow(missing_debug_implementations)]
pub struct BoringTlsConnector {
tls_sni: bool,
enable_ech_grease: bool,
application_settings: bool,
http_version_pref: HttpVersionPref,
connect_layer: ConnectLayer,
ws_connect_layer: Option<ConnectLayer>,
}
impl BoringTlsConnector {
pub fn new(settings: TlsSettings) -> TlsResult<BoringTlsConnector> {
let (connect_layer, ws_connect_layer) = match settings.http_version_pref {
HttpVersionPref::Http1 => {
let connect_layer = create_connect_layer(&settings, settings.http_version_pref)?;
(connect_layer, None)
}
HttpVersionPref::Http2 | HttpVersionPref::All => {
let connect_layer = create_connect_layer(&settings, settings.http_version_pref)?;
let ws_connect_layer = create_connect_layer(&settings, HttpVersionPref::Http1)?;
(connect_layer, Some(ws_connect_layer))
}
};
Ok(Self {
tls_sni: settings.tls_sni,
enable_ech_grease: settings.enable_ech_grease,
application_settings: settings.application_settings,
http_version_pref: settings.http_version_pref,
connect_layer,
ws_connect_layer,
})
}
#[inline]
pub(crate) async fn create_connector(
&self,
http: HttpConnector,
ws: bool,
) -> HttpsConnector<HttpConnector> {
let mut http = HttpsConnector::with_connector_layer(
http,
if ws {
self.ws_connect_layer
.clone()
.unwrap_or_else(|| self.connect_layer.clone())
} else {
self.connect_layer.clone()
},
);
let (application_settings, enable_ech_grease, http_version_pref, tls_sni) = (
self.application_settings,
self.enable_ech_grease,
self.http_version_pref,
self.tls_sni,
);
http.set_callback(move |conf, _| {
conf.configure_enable_ech_grease(enable_ech_grease)?
.set_verify_hostname(tls_sni);
if application_settings {
conf.configure_add_application_settings(http_version_pref)?;
}
Ok(())
});
http
}
}
#[inline]
fn create_connect_layer(
settings: &TlsSettings,
http_version_pref: HttpVersionPref,
) -> TlsResult<ConnectLayer> {
let tls = &settings;
let connector = match &tls.connector {
Some(connector) => connector()?,
None => SslConnector::builder(SslMethod::tls_client())?,
};
let mut connector = connector
.configure_cert_verification(settings.certs_verification)?
.configure_alpn_protos(http_version_pref)?
.configure_min_tls_version(tls.min_tls_version)?
.configure_max_tls_version(tls.max_tls_version)?;
if tls.enable_ocsp_stapling {
connector.enable_ocsp_stapling();
}
if tls.enable_signed_cert_timestamps {
connector.enable_signed_cert_timestamps();
}
if let Some(false) = tls.session_ticket {
connector.set_options(SslOptions::NO_TICKET);
}
if let Some(grease_enabled) = tls.grease_enabled {
connector.set_grease_enabled(grease_enabled);
}
if let Some(permute_extensions) = tls.permute_extensions {
connector.set_permute_extensions(permute_extensions);
}
if let Some(curves) = tls.curves.as_deref() {
connector.set_curves(curves)?;
}
if let Some(sigalgs_list) = tls.sigalgs_list.as_deref() {
connector.set_sigalgs_list(sigalgs_list)?;
}
if let Some(cipher_list) = tls.cipher_list.as_deref() {
connector.set_cipher_list(cipher_list)?;
}
if let Some(cert_compression_algorithm) = tls.cert_compression_algorithm {
connector = connector.configure_add_cert_compression_alg(cert_compression_algorithm)?;
}
let connector = if settings.ca_cert_store.is_none() {
#[cfg(feature = "boring-tls-webpki-roots")]
{
connector.configure_set_webpki_verify_cert_store()?
}
#[cfg(all(
feature = "boring-tls-native-roots",
not(feature = "boring-tls-webpki-roots")
))]
{
connector.configure_set_native_verify_cert_store()?
}
#[cfg(not(any(
feature = "boring-tls-native-roots",
feature = "boring-tls-webpki-roots"
)))]
{
connector
}
} else {
connector.configure_ca_cert_store(settings.ca_cert_store.as_deref())?
};
let settings = HttpsLayerSettings::builder()
.session_cache_capacity(8)
.session_cache(tls.pre_shared_key)
.build();
HttpsLayer::with_connector_and_settings(connector, settings)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Version(SslVersion);
impl Version {
pub const TLS_1_0: Version = Version(SslVersion::TLS1);
pub const TLS_1_1: Version = Version(SslVersion::TLS1_1);
pub const TLS_1_2: Version = Version(SslVersion::TLS1_2);
pub const TLS_1_3: Version = Version(SslVersion::TLS1_3);
}
#[derive(Debug, Clone)]
pub struct TlsInfo {
pub(crate) peer_certificate: Option<Vec<u8>>,
}
impl TlsInfo {
pub fn peer_certificate(&self) -> Option<&[u8]> {
self.peer_certificate.as_ref().map(|der| &der[..])
}
}