General Upgrade, NEW: WebServers, Better Docs
Some checks failed
CI / checks (push) Failing after 5m18s
Some checks failed
CI / checks (push) Failing after 5m18s
This commit is contained in:
parent
5f11d476b6
commit
6e5c985719
122 changed files with 10309 additions and 5206 deletions
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue