General Upgrade, NEW: WebServers, Better Docs
Some checks failed
CI / checks (push) Failing after 5m18s

This commit is contained in:
Alex Emmet 2026-07-18 03:08:03 +02:00
commit 6e5c985719
122 changed files with 10309 additions and 5206 deletions

View file

@ -1,61 +1,124 @@
use std::sync::Arc;
use mtp_common::CommunicationError;
use rustls::{
ClientConfig as RustlsClientConfig, DigitallySignedStruct, RootCertStore, SignatureScheme,
client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
pki_types::{ServerName, UnixTime, pem::PemObject},
};
use wtransport::{ClientConfig, Endpoint};
use rustls::{ClientConfig as RustlsClientConfig, RootCertStore, pki_types::pem::PemObject};
use wtransport::{ClientConfig as WTransportClientConfig, Endpoint};
use crate::{ConnectionHandle, Policy, Receiver, Sender};
#[derive(Debug)]
struct NoopCertVerifier;
#[cfg(feature = "insecure-tls")]
mod noop_verifier {
use rustls::{
DigitallySignedStruct, SignatureScheme,
client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
pki_types::{CertificateDer, ServerName, UnixTime},
};
impl ServerCertVerifier for NoopCertVerifier {
fn verify_server_cert(
&self,
_end_entity: &rustls::pki_types::CertificateDer<'_>,
_intermediates: &[rustls::pki_types::CertificateDer<'_>],
_server_name: &ServerName<'_>,
_ocsp_response: &[u8],
_now: UnixTime,
) -> Result<ServerCertVerified, rustls::Error> {
Ok(ServerCertVerified::assertion())
#[derive(Debug)]
pub(super) struct NoopCertVerifier;
impl ServerCertVerifier for NoopCertVerifier {
fn verify_server_cert(
&self,
_end_entity: &CertificateDer<'_>,
_intermediates: &[CertificateDer<'_>],
_server_name: &ServerName<'_>,
_ocsp_response: &[u8],
_now: UnixTime,
) -> Result<ServerCertVerified, rustls::Error> {
Ok(ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &CertificateDer<'_>,
_dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
Ok(HandshakeSignatureValid::assertion())
}
fn verify_tls13_signature(
&self,
_message: &[u8],
_cert: &CertificateDer<'_>,
_dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
Ok(HandshakeSignatureValid::assertion())
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
vec![
SignatureScheme::RSA_PKCS1_SHA1,
SignatureScheme::RSA_PKCS1_SHA256,
SignatureScheme::RSA_PKCS1_SHA384,
SignatureScheme::RSA_PKCS1_SHA512,
SignatureScheme::ECDSA_NISTP256_SHA256,
SignatureScheme::ECDSA_NISTP384_SHA384,
SignatureScheme::RSA_PSS_SHA256,
SignatureScheme::RSA_PSS_SHA384,
SignatureScheme::RSA_PSS_SHA512,
SignatureScheme::ED25519,
]
}
}
}
/// TLS and transport settings for a native client connection.
///
/// Certificate verification uses system roots by default. Disabling verification
/// requires an explicit call to [`Self::with_insecure_certificate_verification`]
/// and the `insecure-tls` compile-time feature.
pub struct ClientConfig {
server_cert: Option<Vec<u8>>,
pinned_hash: Option<[u8; 32]>,
insecure_certificate_verification: bool,
policy: Policy,
}
impl ClientConfig {
pub fn new(policy: Policy) -> Self {
Self {
server_cert: None,
pinned_hash: None,
insecure_certificate_verification: false,
policy,
}
}
fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &rustls::pki_types::CertificateDer<'_>,
_dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
Ok(HandshakeSignatureValid::assertion())
pub fn with_server_certificate(mut self, cert_pem: Vec<u8>) -> Self {
self.server_cert = Some(cert_pem);
self.insecure_certificate_verification = false;
self.pinned_hash = None;
self
}
fn verify_tls13_signature(
&self,
_message: &[u8],
_cert: &rustls::pki_types::CertificateDer<'_>,
_dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
Ok(HandshakeSignatureValid::assertion())
/// Pin the connection to a specific SPKI SHA-256 hash.
///
/// The client will only accept server certificates whose DER-encoded Subject
/// Public Key Info matches the given 32-byte hash. This is the recommended
/// approach for trusting self-signed certificates without disabling
/// verification entirely.
pub fn with_pinned_certificate_hash(mut self, hash: [u8; 32]) -> Self {
self.pinned_hash = Some(hash);
self.server_cert = None;
self.insecure_certificate_verification = false;
self
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
vec![
SignatureScheme::RSA_PKCS1_SHA1,
SignatureScheme::RSA_PKCS1_SHA256,
SignatureScheme::RSA_PKCS1_SHA384,
SignatureScheme::RSA_PKCS1_SHA512,
SignatureScheme::ECDSA_NISTP256_SHA256,
SignatureScheme::ECDSA_NISTP384_SHA384,
SignatureScheme::RSA_PSS_SHA256,
SignatureScheme::RSA_PSS_SHA384,
SignatureScheme::RSA_PSS_SHA512,
SignatureScheme::ED25519,
]
/// Disable server certificate verification.
///
/// Requires the `insecure-tls` feature at compile time and the environment
/// variable `MTP_INSECURE_TLS=1` at runtime. Returns an error if either
/// condition is not met.
///
/// Intended only for local development with a self-signed host.
#[cfg(feature = "insecure-tls")]
pub fn with_insecure_certificate_verification(mut self) -> Self {
self.server_cert = None;
self.pinned_hash = None;
self.insecure_certificate_verification = true;
self
}
}
@ -63,13 +126,47 @@ pub async fn connect(
url: &str,
server_cert: Option<Vec<u8>>,
policy: Policy,
) -> Result<(Sender, Receiver), CommunicationError> {
let config = match server_cert {
Some(cert_pem) => ClientConfig::new(policy).with_server_certificate(cert_pem),
None => ClientConfig::new(policy),
};
connect_with_config(url, config).await
}
/// Connect using explicit TLS and transport configuration.
pub async fn connect_with_config(
url: &str,
config: ClientConfig,
) -> Result<(Sender, Receiver), CommunicationError> {
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
let client_config = if let Some(cert_pem) = server_cert {
configure_client_with_cert(cert_pem, &policy)?
let client_config = if config.insecure_certificate_verification {
#[cfg(feature = "insecure-tls")]
{
let env_val = std::env::var("MTP_INSECURE_TLS")
.map(|v| v == "1")
.unwrap_or(false);
if !env_val {
return Err(CommunicationError::Other(
"insecure TLS requires MTP_INSECURE_TLS=1 in the environment".into(),
));
}
client_config_insecure(&config.policy)?
}
#[cfg(not(feature = "insecure-tls"))]
{
unreachable!(
"insecure_certificate_verification is only set when \
the insecure-tls feature is enabled"
)
}
} else if let Some(hash) = config.pinned_hash {
crate::pinning::configure_client_pinned_hash(hash, &config.policy)?
} else if let Some(cert_pem) = config.server_cert {
configure_client_with_cert(cert_pem, &config.policy)?
} else {
configure_client_system_roots(&policy)?
configure_client_system_roots(&config.policy)?
};
let endpoint = Endpoint::client(client_config)
@ -81,7 +178,7 @@ pub async fn connect(
.map_err(|e| CommunicationError::ConnectingError(e.to_string()))?;
let handle = Arc::new(ConnectionHandle::new());
let policy = Arc::new(policy);
let policy = Arc::new(config.policy);
let sender = Sender::new(connection.clone(), handle.clone(), policy.clone());
let receiver = Receiver::new(connection, handle, policy);
@ -92,7 +189,7 @@ pub async fn connect(
fn configure_client_with_cert(
server_cert: Vec<u8>,
policy: &Policy,
) -> Result<ClientConfig, CommunicationError> {
) -> Result<WTransportClientConfig, CommunicationError> {
let mut root_store = RootCertStore::empty();
let certs = rustls::pki_types::CertificateDer::pem_slice_iter(&server_cert)
@ -108,7 +205,10 @@ fn configure_client_with_cert(
client_config_from_roots(root_store, policy)
}
fn client_config_insecure(policy: &Policy) -> Result<ClientConfig, CommunicationError> {
#[cfg(feature = "insecure-tls")]
fn client_config_insecure(policy: &Policy) -> Result<WTransportClientConfig, CommunicationError> {
use noop_verifier::NoopCertVerifier;
let mut tls_config = RustlsClientConfig::builder()
.dangerous()
.with_custom_certificate_verifier(Arc::new(NoopCertVerifier))
@ -116,7 +216,7 @@ fn client_config_insecure(policy: &Policy) -> Result<ClientConfig, Communication
tls_config.alpn_protocols = vec![b"h3".to_vec()];
Ok(ClientConfig::builder()
Ok(WTransportClientConfig::builder()
.with_bind_default()
.with_custom_tls(tls_config)
.keep_alive_interval(policy.keep_alive_interval)
@ -125,16 +225,11 @@ fn client_config_insecure(policy: &Policy) -> Result<ClientConfig, Communication
.build())
}
fn configure_client_system_roots(policy: &Policy) -> Result<ClientConfig, CommunicationError> {
// Check if insecure mode is enabled via env variable MTP_INSECURE
let insecure = std::env::var("MTP_INSECURE").is_ok();
if insecure {
// Insecure mode: skip certificate verification entirely
return client_config_insecure(policy);
}
fn configure_client_system_roots(
policy: &Policy,
) -> Result<WTransportClientConfig, CommunicationError> {
let mut root_store = RootCertStore::empty();
// Load native certs
let certs = rustls_native_certs::load_native_certs().certs;
for cert in certs {
@ -147,14 +242,14 @@ fn configure_client_system_roots(policy: &Policy) -> Result<ClientConfig, Commun
fn client_config_from_roots(
root_store: RootCertStore,
policy: &Policy,
) -> Result<ClientConfig, CommunicationError> {
) -> Result<WTransportClientConfig, CommunicationError> {
let mut tls_config = RustlsClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth();
tls_config.alpn_protocols = vec![b"h3".to_vec()];
Ok(ClientConfig::builder()
Ok(WTransportClientConfig::builder()
.with_bind_default()
.with_custom_tls(tls_config)
.keep_alive_interval(policy.keep_alive_interval)