diff --git a/Cargo.toml b/Cargo.toml index 7335efa..2234c0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,6 @@ http = "1" chrono = "0.4.38" env_logger = "0.11.3" rustainers = "0.12.0" -rustls = { version = "0.22.4" } +rustls = { version = "0.23" } tokio = { version = "1.37.0", features = ["rt", "macros"] } ureq = "=3.0.0-rc2" diff --git a/generator/src/main/resources/crust/Cargo.mustache b/generator/src/main/resources/crust/Cargo.mustache index 1293c82..797ec84 100644 --- a/generator/src/main/resources/crust/Cargo.mustache +++ b/generator/src/main/resources/crust/Cargo.mustache @@ -49,6 +49,6 @@ http = "1" chrono = "0.4.38" env_logger = "0.11.3" rustainers = "0.12.0" -rustls = { version = "0.22.4" } +rustls = { version = "0.23.4" } tokio = { version = "1.37.0", features = ["rt", "macros"] } ureq = "=3.0.0-rc2" diff --git a/tests/utils/mod.rs b/tests/utils/mod.rs index 2ffeb82..7c64cce 100644 --- a/tests/utils/mod.rs +++ b/tests/utils/mod.rs @@ -3,7 +3,11 @@ // Author: David Runge // License: Apache-2.0 OR MIT -use std::sync::Arc; +use std::{ + fmt::Debug, + io::{Read, Write}, + sync::Arc, +}; use nethsm_sdk_rs::apis::configuration::Configuration; use rustainers::{ @@ -12,15 +16,174 @@ use rustainers::{ WaitStrategy, }; use rustls::{ - client::{ - danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}, - ClientConfig, + client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}, + crypto::{ + self, verify_tls12_signature, verify_tls13_signature, CryptoProvider, + WebPkiSupportedAlgorithms, }, - crypto::{self, WebPkiSupportedAlgorithms}, pki_types::{CertificateDer, ServerName, UnixTime}, - DigitallySignedStruct, SignatureScheme, + ClientConnection, DigitallySignedStruct, SignatureScheme, StreamOwned, +}; +use ureq::{ + resolver::DefaultResolver, + transport::{ + Buffers, ChainedConnector, ConnectionDetails, Connector, LazyBuffers, NextTimeout, + TcpConnector, Transport, TransportAdapter, + }, }; +#[derive(Debug)] +struct TlsConnectorAcceptAll; + +impl Connector for TlsConnectorAcceptAll { + fn connect( + &self, + details: &ConnectionDetails, + chained: Option>, + ) -> Result>, ureq::Error> { + let Some(transport) = chained else { + panic!("Chained connector is required"); + }; + + if !details.needs_tls() || transport.is_tls() { + return Ok(Some(transport)); + } + + let name_borrowed: ServerName<'_> = details + .uri + .authority() + .expect("uri authority for tls") + .host() + .try_into() + .map_err(|_e| ureq::Error::Tls("Rustls invalid dns name error"))?; + + let name = name_borrowed.to_owned(); + let config = rustls_accept_all_config(); + let conn = ClientConnection::new(config, name)?; + + let stream = StreamOwned { + conn, + sock: TransportAdapter::new(transport), + }; + + // FIXME: use details.config.(input/output)_buffer_size, + let buffers = LazyBuffers::new(512 * 1024, 512 * 1024); + + let transport = Box::new(CertIgnoredTransport { buffers, stream }); + Ok(Some(transport)) + } +} + +struct CertIgnoredTransport { + stream: StreamOwned, + buffers: LazyBuffers, +} + +impl Debug for CertIgnoredTransport { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CertIgnoredTransport") + .finish_non_exhaustive() + } +} + +impl Transport for CertIgnoredTransport { + fn buffers(&mut self) -> &mut dyn Buffers { + &mut self.buffers + } + + fn transmit_output(&mut self, amount: usize, timeout: NextTimeout) -> Result<(), ureq::Error> { + self.stream.get_mut().set_timeout(timeout); + + let output = &self.buffers.output()[..amount]; + self.stream.write_all(output)?; + + Ok(()) + } + + fn await_input(&mut self, timeout: NextTimeout) -> Result { + if self.buffers.can_use_input() { + return Ok(true); + } + + self.stream.get_mut().set_timeout(timeout); + + let input = self.buffers.input_append_buf(); + let amount = self.stream.read(input)?; + self.buffers.input_appended(amount); + + Ok(amount > 0) + } + + fn is_open(&mut self) -> bool { + self.stream.get_mut().get_mut().is_open() + } + + fn is_tls(&self) -> bool { + true + } +} + +fn rustls_accept_all_config() -> Arc { + let provider = Arc::new(rustls::crypto::ring::default_provider()); + + let config = rustls::ClientConfig::builder_with_provider(provider.clone()) + .with_protocol_versions(rustls::ALL_VERSIONS) + .unwrap() + .dangerous() + .with_custom_certificate_verifier(Arc::new(DisabledVerifier(provider.clone()))) + .with_no_client_auth(); + + Arc::new(config) +} + +#[derive(Debug)] +struct DisabledVerifier(Arc); + +impl ServerCertVerifier for DisabledVerifier { + fn verify_server_cert( + &self, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, + _ocsp_response: &[u8], + _now: UnixTime, + ) -> Result { + Ok(rustls::client::danger::ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + verify_tls12_signature( + message, + cert, + dss, + &self.0.signature_verification_algorithms, + ) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + verify_tls13_signature( + message, + cert, + dss, + &self.0.signature_verification_algorithms, + ) + } + + fn supported_verify_schemes(&self) -> Vec { + self.0.signature_verification_algorithms.supported_schemes() + } +} + pub async fn with_container T, T>(f: F) -> T { let _ = env_logger::builder().is_test(true).try_init(); @@ -33,14 +196,14 @@ pub async fn with_container T, T>(f: F) -> T { let config = Configuration { base_path: container.api().await, - client: ureq::Agent::config_builder() - .tls_config( - ureq::tls::TlsConfig::builder() - .disable_verification(true) - .build(), - ) - .build() - .new_agent(), + client: ureq::Agent::with_parts( + ureq::Agent::config_builder().build(), + ChainedConnector::new([ + Box::new(TcpConnector::default()) as Box, + Box::new(TlsConnectorAcceptAll), + ]), + DefaultResolver::default(), + ), ..Default::default() };