From 513e0e903bef388a4785b772fd17f86208c923c4 Mon Sep 17 00:00:00 2001 From: Manuthor Date: Tue, 23 Dec 2025 08:35:13 +0100 Subject: [PATCH 1/3] feat(http_client): Replace rustls with native-tls for FIPS compliance - Remove rustls and AWS-LC dependencies in favor of native-tls (OpenSSL) - Delete certificate_verifier.rs (rustls-specific custom verification) - Update Cargo.toml to disable default reqwest features and use only native-tls - Remove TEE certificate verification support (requires rustls) - Remove client-side custom cipher suite support (server-side only) - Update PEM certificate loading to use from_pkcs8_pem - Add warnings for unsupported features when native-tls is used - Simplify TLS configuration code by removing rustls complexity Breaking changes: - TEE (Trusted Execution Environment) certificate verification no longer supported - Client-side cipher suite configuration no longer supported - Requires OpenSSL library at runtime for FIPS compliance This change ensures FIPS 140-3 compliance when built against a FIPS-validated OpenSSL. --- .pre-commit-config.yaml | 2 +- CHANGELOG.md | 12 + Cargo.lock | 162 +-------- Cargo.toml | 2 +- README.md | 6 +- crate/http_client/Cargo.toml | 16 +- crate/http_client/src/certificate_verifier.rs | 155 -------- crate/http_client/src/http_client.rs | 8 +- crate/http_client/src/lib.rs | 1 - crate/http_client/src/tls.rs | 341 +++--------------- crate/logger/README.md | 8 +- crate/logger/src/lib.rs | 2 +- 12 files changed, 90 insertions(+), 625 deletions(-) delete mode 100644 crate/http_client/src/certificate_verifier.rs diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3379de1..affddf7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -141,7 +141,7 @@ repos: - id: nightly-cargo-format # in last du to clippy fixes - repo: https://github.com/EmbarkStudios/cargo-deny - rev: 0.18.2 # choose your preferred tag + rev: 0.18.9 hooks: - id: cargo-deny args: [--all-features, check] diff --git a/CHANGELOG.md b/CHANGELOG.md index 1985508..4ee4098 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ All notable changes to this project will be documented in this file. +## [0.7.0] - 2025-12-23 + +### 🚀 Features + +- *(http_client)* Replace rustls with native-tls for FIPS 140-3 compliance + +### ⚠️ Breaking Changes + +- *(http_client)* Removed TEE (Trusted Execution Environment) certificate verification support (rustls-specific feature incompatible with native-tls) +- *(http_client)* Removed client-side custom cipher suite configuration (not supported by native-tls; server-side configuration remains available) +- *(http_client)* Now requires OpenSSL library at runtime for FIPS compliance + ## [0.6.0] - 2025-12-06 ### ⚙️ Miscellaneous Tasks diff --git a/Cargo.lock b/Cargo.lock index b40db1c..185dc25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -321,28 +321,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "aws-lc-rs" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b5ce75405893cd713f9ab8e297d8e438f624dde7d706108285f7e17a25a180f" -dependencies = [ - "aws-lc-sys", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "179c3777a8b5e70e90ea426114ffc565b2c1a9f82f6c4a0c5a34aa6ef5e781b6" -dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", -] - [[package]] name = "base64" version = "0.20.0" @@ -442,12 +420,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - [[package]] name = "chrono" version = "0.4.42" @@ -472,15 +444,6 @@ dependencies = [ "inout", ] -[[package]] -name = "cmake" -version = "0.1.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" -dependencies = [ - "cc", -] - [[package]] name = "const-oid" version = "0.9.6" @@ -532,7 +495,7 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cosmian_config_utils" -version = "0.6.0" +version = "0.7.0" dependencies = [ "base64 0.21.7", "serde", @@ -545,7 +508,7 @@ dependencies = [ [[package]] name = "cosmian_http_client" -version = "0.6.0" +version = "0.7.0" dependencies = [ "actix-http", "actix-identity", @@ -556,20 +519,18 @@ dependencies = [ "derive_more", "oauth2", "reqwest", - "rustls", "serde", "serde_json", "thiserror 2.0.17", "tokio", "tracing", "url", - "webpki-roots", "x509-cert", ] [[package]] name = "cosmian_logger" -version = "0.6.0" +version = "0.7.0" dependencies = [ "opentelemetry", "opentelemetry-otlp", @@ -716,12 +677,6 @@ dependencies = [ "syn", ] -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - [[package]] name = "either" version = "1.15.0" @@ -817,12 +772,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - [[package]] name = "futures-channel" version = "0.3.31" @@ -926,11 +875,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi", "wasip2", - "wasm-bindgen", ] [[package]] @@ -1109,7 +1056,6 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots", ] [[package]] @@ -1441,12 +1387,6 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - [[package]] name = "matchers" version = "0.2.0" @@ -1847,61 +1787,6 @@ dependencies = [ "syn", ] -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2 0.6.1", - "thiserror 2.0.17", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" -dependencies = [ - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.2", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.17", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2 0.6.1", - "tracing", - "windows-sys 0.60.2", -] - [[package]] name = "quote" version = "1.0.42" @@ -2028,7 +1913,6 @@ checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" dependencies = [ "base64 0.22.1", "bytes", - "encoding_rs", "futures-channel", "futures-core", "futures-util", @@ -2042,12 +1926,9 @@ dependencies = [ "hyper-util", "js-sys", "log", - "mime", "native-tls", "percent-encoding", "pin-project-lite", - "quinn", - "rustls", "rustls-pki-types", "serde", "serde_json", @@ -2055,7 +1936,6 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-native-tls", - "tokio-rustls", "tower 0.5.2", "tower-http", "tower-service", @@ -2063,7 +1943,6 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", ] [[package]] @@ -2080,12 +1959,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - [[package]] name = "rustc_version" version = "0.4.1" @@ -2114,10 +1987,7 @@ version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ - "aws-lc-rs", - "log", "once_cell", - "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -2130,7 +2000,6 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" dependencies = [ - "web-time", "zeroize", ] @@ -2140,7 +2009,6 @@ version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ - "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -2549,21 +2417,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tls_codec" version = "0.4.2" @@ -3063,15 +2916,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki-roots" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "windows-core" version = "0.62.2" diff --git a/Cargo.toml b/Cargo.toml index c466540..1fbf7b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["crate/config_utils", "crate/logger", "crate/http_client"] resolver = "2" [workspace.package] -version = "0.6.0" +version = "0.7.0" edition = "2021" rust-version = "1.71.0" authors = [ diff --git a/README.md b/README.md index a7653ba..d56f3e3 100644 --- a/README.md +++ b/README.md @@ -40,9 +40,9 @@ To use these crates in your project, add them to your `Cargo.toml`: ```toml [dependencies] -cosmian_http_client = { version = "0.6.0", features = ["session"] } -cosmian_logger = "0.6.0" -cosmian_config_utils = "0.6.0" +cosmian_http_client = { version = "0.7.0", features = ["session"] } +cosmian_logger = "0.7.0" +cosmian_config_utils = "0.7.0" ``` ## License diff --git a/crate/http_client/Cargo.toml b/crate/http_client/Cargo.toml index 396b62c..c8b2cf9 100644 --- a/crate/http_client/Cargo.toml +++ b/crate/http_client/Cargo.toml @@ -14,6 +14,7 @@ description = "HTTP client with multi-authentication support" doctest = false [features] +default = [] session = ["dep:actix-identity", "dep:actix-session"] [dependencies] @@ -21,22 +22,25 @@ actix-identity = { version = "0.9", optional = true } actix-session = { version = "0.11", optional = true } actix-web = { version = "4.9", features = ["macros"] } derive_more = { version = "2.1", features = ["deref", "deref_mut"] } -oauth2 = { version = "5.0", features = ["reqwest"] } -reqwest = { version = "0.12", features = [ - "default", +# Disable default features to avoid pulling in rustls +# The "reqwest" feature will still use our reqwest dependency (with native-tls only) +oauth2 = { version = "5.0", default-features = false, features = ["reqwest"] } +# For FIPS compliance: use only native-tls (OpenSSL), not rustls (AWS-LC) +# Removed "default" (includes rustls) and "rustls-tls" to avoid conflicts +# Need "blocking" for Identity::from_pem +reqwest = { version = "0.12", default-features = false, features = [ "json", - "rustls-tls", "native-tls", "socks", + "http2", + "macos-system-configuration", ] } -rustls = { version = "0.23", features = ["aws-lc-rs"] } serde = { workspace = true } serde_json = { workspace = true } thiserror = { workspace = true } tokio = { version = "1.47", features = ["full"] } tracing = "0.1" url = "2.5" -webpki-roots = "1.0" x509-cert = "0.2.5" [dev-dependencies] diff --git a/crate/http_client/src/certificate_verifier.rs b/crate/http_client/src/certificate_verifier.rs deleted file mode 100644 index 74929e0..0000000 --- a/crate/http_client/src/certificate_verifier.rs +++ /dev/null @@ -1,155 +0,0 @@ -use std::sync::Arc; - -use rustls::{ - client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}, - crypto::{verify_tls12_signature, verify_tls13_signature, CryptoProvider}, - pki_types::{CertificateDer, ServerName, UnixTime}, - DigitallySignedStruct, Error as RustTLSError, SignatureScheme, -}; - -/// A TLS verifier adding the ability to match the leaf certificate with a -/// trusted one. -#[derive(Debug)] -pub(crate) struct LeafCertificateVerifier { - // The certificate we expect to see in the TLS connection - expected_cert: CertificateDer<'static>, - // A default verifier to run anyway - default_verifier: Arc, - // Crypto provider for signature verification - provider: Arc, -} - -impl LeafCertificateVerifier { - pub(crate) fn new( - expected_cert: CertificateDer<'static>, - default_verifier: Arc, - ) -> Self { - Self { - expected_cert, - default_verifier, - provider: Arc::new(rustls::crypto::aws_lc_rs::default_provider()), - } - } -} - -impl ServerCertVerifier for LeafCertificateVerifier { - fn verify_server_cert( - &self, - end_entity: &CertificateDer<'_>, - intermediates: &[CertificateDer<'_>], - server_name: &ServerName<'_>, - ocsp_response: &[u8], - now: UnixTime, - ) -> Result { - // Verify the leaf certificate - if end_entity.as_ref() != self.expected_cert.as_ref() { - return Err(RustTLSError::General( - "Leaf certificate doesn't match the expected one".to_owned(), - )); - } - - // Now proceed with typical verifications - self.default_verifier.verify_server_cert( - end_entity, - intermediates, - server_name, - ocsp_response, - now, - ) - } - - fn verify_tls12_signature( - &self, - message: &[u8], - cert: &CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> Result { - verify_tls12_signature( - message, - cert, - dss, - &self.provider.signature_verification_algorithms, - ) - } - - fn verify_tls13_signature( - &self, - message: &[u8], - cert: &CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> Result { - verify_tls13_signature( - message, - cert, - dss, - &self.provider.signature_verification_algorithms, - ) - } - - fn supported_verify_schemes(&self) -> Vec { - self.provider - .signature_verification_algorithms - .supported_schemes() - } -} - -/// Remove all verifications -#[derive(Debug)] -pub(crate) struct NoVerifier { - provider: Arc, -} - -impl NoVerifier { - pub(crate) fn new() -> Self { - Self { - provider: Arc::new(rustls::crypto::aws_lc_rs::default_provider()), - } - } -} - -impl ServerCertVerifier for NoVerifier { - fn verify_server_cert( - &self, - _: &CertificateDer<'_>, - _: &[CertificateDer<'_>], - _: &ServerName<'_>, - _: &[u8], - _: UnixTime, - ) -> Result { - Ok(ServerCertVerified::assertion()) - } - - fn verify_tls12_signature( - &self, - message: &[u8], - cert: &CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> Result { - verify_tls12_signature( - message, - cert, - dss, - &self.provider.signature_verification_algorithms, - ) - } - - fn verify_tls13_signature( - &self, - message: &[u8], - cert: &CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> Result { - verify_tls13_signature( - message, - cert, - dss, - &self.provider.signature_verification_algorithms, - ) - } - - fn supported_verify_schemes(&self) -> Vec { - self.provider - .signature_verification_algorithms - .supported_schemes() - } -} diff --git a/crate/http_client/src/http_client.rs b/crate/http_client/src/http_client.rs index b07f454..13c5182 100644 --- a/crate/http_client/src/http_client.rs +++ b/crate/http_client/src/http_client.rs @@ -75,7 +75,9 @@ pub struct HttpClientConfig { #[serde(skip_serializing_if = "Option::is_none")] pub proxy_params: Option, /// Colon-separated list of cipher suites to use for TLS connections. - /// If not specified, rustls safe defaults will be used. + /// Note: Custom cipher suites are not supported with native-tls. + /// Server-side cipher suite configuration is available through server + /// configuration. /// /// Example: "`TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256`" #[serde(skip_serializing_if = "Option::is_none")] @@ -165,8 +167,8 @@ impl HttpClient { headers.insert("DatabaseSecret", HeaderValue::from_str(&database_secret)?); } - // Build a TLS client builder with rustls backend compatible with TLS 1.3 and - // 1.2 + // Build a TLS client builder with native-tls backend compatible with TLS 1.3 + // and 1.2 let mut builder = build_tls_client(http_conf)?; // Apply proxy settings if configured diff --git a/crate/http_client/src/lib.rs b/crate/http_client/src/lib.rs index be1fdd2..984dd67 100644 --- a/crate/http_client/src/lib.rs +++ b/crate/http_client/src/lib.rs @@ -59,7 +59,6 @@ pub use login::{LoginState, Oauth2LoginConfig}; pub use proxy_params::ProxyParams; pub mod authentication; -mod certificate_verifier; mod error; mod http_client; mod login; diff --git a/crate/http_client/src/tls.rs b/crate/http_client/src/tls.rs index e565183..9717c15 100644 --- a/crate/http_client/src/tls.rs +++ b/crate/http_client/src/tls.rs @@ -1,50 +1,31 @@ use std::{ fs::File, io::{BufReader, Read}, - sync::Arc, }; -use reqwest::{Client, ClientBuilder, Identity}; -use rustls::{ - client::danger::ServerCertVerifier, pki_types::CertificateDer, RootCertStore, - SupportedCipherSuite, -}; -use x509_cert::{ - der::{DecodePem, Encode}, - Certificate as X509Certificate, -}; +use reqwest::{ClientBuilder, Identity}; -use crate::{ - certificate_verifier::{LeafCertificateVerifier, NoVerifier}, - error::result::HttpClientResult, - HttpClientConfig, HttpClientError, -}; +use crate::{error::result::HttpClientResult, HttpClientConfig}; -/// Comprehensive TLS client builder that handles all TLS configuration -/// scenarios +/// TLS client builder using native-tls (OpenSSL) for FIPS compliance /// -/// This function consolidates all TLS configuration logic into a single, -/// maintainable function. It replaces the complex nested conditionals that were -/// previously in `http_client.rs`. +/// This function builds a TLS client with native-tls backend, which uses +/// the system's OpenSSL library. This approach ensures FIPS 140-3 compliance +/// when built against a FIPS-validated OpenSSL. /// /// # Supported TLS Scenarios: /// -/// 1. **TEE Certificate Verification**: When `verified_cert` is provided, -/// builds a TLS client with custom leaf certificate verification for Trusted -/// Execution Environment contexts. The certificate verification includes -/// both standard CA verification and specific leaf certificate validation. +/// 1. **Default TLS Configuration**: Uses standard TLS settings with optional +/// invalid certificate acceptance based on `accept_invalid_certs`. /// -/// 2. **Custom Cipher Suites**: When `cipher_suites` is specified, configures -/// the TLS client to use only the specified cipher suites. Falls back to -/// default configuration if cipher suite parsing fails. +/// 2. **PEM Client Certificate Authentication**: When +/// `ssl_client_pem_cert_path` and `ssl_client_pem_key_path` are provided, +/// loads and configures client certificate authentication using PEM format +/// (FIPS compatible). /// -/// 3. **Default TLS Configuration**: When no special configuration is needed, -/// uses standard TLS settings with optional invalid certificate acceptance -/// based on `accept_invalid_certs`. -/// -/// 4. **PKCS12 Client Certificate Authentication**: When +/// 3. **PKCS12 Client Certificate Authentication**: When /// `ssl_client_pkcs12_path` is provided, loads and configures client -/// certificate authentication using PKCS12 format. +/// certificate authentication using PKCS12 format (non-FIPS mode only). /// /// # Parameters /// * `http_conf` - HTTP client configuration containing TLS settings @@ -53,44 +34,41 @@ use crate::{ /// * `HttpClientResult` - Configured reqwest `ClientBuilder` /// ready for use /// -/// # Error Handling -/// The function is designed to be robust - if any step fails, it logs the error -/// and falls back to a safe default configuration rather than failing -/// completely. +/// # Limitations +/// - TEE certificate verification is not supported (would require rustls) +/// - Custom cipher suites are not supported on the client side (server-side +/// only) pub(crate) fn build_tls_client(http_conf: &HttpClientConfig) -> HttpClientResult { - // Step 1: Handle TEE certificate verification - let builder = if let Some(certificate) = &http_conf.verified_cert { - let tee_cert = - CertificateDer::from(X509Certificate::from_pem(certificate.as_bytes())?.to_der()?); - build_tls_client_tee( - tee_cert, - http_conf.accept_invalid_certs, - http_conf.cipher_suites.as_deref(), - ) - } else if let Some(cipher_suites_str) = &http_conf.cipher_suites { - // Step 2: Handle custom cipher suites - match parse_cipher_suites(cipher_suites_str) { - Ok(cipher_suites) => { - match build_tls_config(Some(&cipher_suites), http_conf.accept_invalid_certs) { - Ok(config) => Client::builder().use_preconfigured_tls(config), - Err(e) => { - tracing::error!("TLS config error: {e}, falling back to safe defaults"); - ClientBuilder::new() - .danger_accept_invalid_certs(http_conf.accept_invalid_certs) - } - } - } - Err(e) => { - tracing::error!("Cipher suite parsing error: {e}, falling back to safe defaults"); - ClientBuilder::new().danger_accept_invalid_certs(http_conf.accept_invalid_certs) - } - } - } else { - // Default configuration - ClientBuilder::new().danger_accept_invalid_certs(http_conf.accept_invalid_certs) - }; + // Warn if advanced features are requested but not available + if http_conf.verified_cert.is_some() { + tracing::warn!( + "TEE certificate verification is not supported with native-tls. Falling back to \ + standard certificate verification." + ); + } + + if http_conf.cipher_suites.is_some() { + tracing::warn!( + "Custom cipher suites are not supported on the client side with native-tls. \ + Server-side cipher suite configuration is available through server configuration. \ + Using default cipher suites." + ); + } - // Step 3: Handle client certificate authentication + // Build basic native-tls client + let builder = ClientBuilder::new().danger_accept_invalid_certs(http_conf.accept_invalid_certs); + + // Handle client certificate authentication (PEM or PKCS#12) + let builder = add_client_identity(builder, http_conf)?; + + Ok(builder) +} + +/// Add client identity (certificate) to the builder if configured +fn add_client_identity( + builder: ClientBuilder, + http_conf: &HttpClientConfig, +) -> HttpClientResult { // Prefer PEM (cert + key) if provided; otherwise fall back to PKCS#12 let builder = if let (Some(cert_path), Some(key_path)) = ( http_conf.ssl_client_pem_cert_path.as_deref(), @@ -104,15 +82,9 @@ pub(crate) fn build_tls_client(http_conf: &HttpClientConfig) -> HttpClientResult let mut key_bytes = vec![]; key_reader.read_to_end(&mut key_bytes)?; - // Combine cert and key into a single PEM as expected by reqwest Identity - let mut pem = Vec::with_capacity(cert_bytes.len() + 1 + key_bytes.len()); - pem.extend_from_slice(&cert_bytes); - if !pem.ends_with(b"\n") { - pem.push(b'\n'); - } - pem.extend_from_slice(&key_bytes); - - let identity = Identity::from_pem(&pem)?; + // Create identity from certificate and key PEM files + // from_pkcs8_pem expects (cert_pem, key_pem) separately + let identity = Identity::from_pkcs8_pem(&cert_bytes, &key_bytes)?; builder.identity(identity) } else if let Some(ssl_client_pkcs12) = &http_conf.ssl_client_pkcs12_path { let mut pkcs12 = BufReader::new(File::open(ssl_client_pkcs12)?); @@ -132,216 +104,3 @@ pub(crate) fn build_tls_client(http_conf: &HttpClientConfig) -> HttpClientResult Ok(builder) } - -/// Parse cipher suites string and return a vector of supported cipher suites -fn parse_cipher_suites(cipher_suites_str: &str) -> HttpClientResult> { - let mut selected_suites = Vec::new(); - - // Split the cipher suites string by colon only - let suite_names: Vec<&str> = cipher_suites_str - .split(':') - .map(str::trim) - .filter(|s| !s.is_empty()) - .collect(); - - for suite_name in suite_names { - // Map common cipher suite names to rustls cipher suites - let cipher_suite = match suite_name.to_uppercase().as_str() { - "TLS_AES_256_GCM_SHA384" => { - Some(rustls::crypto::aws_lc_rs::cipher_suite::TLS13_AES_256_GCM_SHA384) - } - "TLS_AES_128_GCM_SHA256" => { - Some(rustls::crypto::aws_lc_rs::cipher_suite::TLS13_AES_128_GCM_SHA256) - } - "TLS_CHACHA20_POLY1305_SHA256" => { - Some(rustls::crypto::aws_lc_rs::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256) - } - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" => { - Some(rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) - } - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" => { - Some(rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) - } - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" => Some( - rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - ), - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" => { - Some(rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) - } - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" => { - Some(rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) - } - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" => { - Some(rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) - } - _ => { - return Err(HttpClientError::Default(format!( - "Unknown cipher suite: {suite_name}" - ))); - } - }; - - if let Some(suite) = cipher_suite { - selected_suites.push(suite); - } - } - - if selected_suites.is_empty() { - return Err(HttpClientError::Default( - "No valid cipher suites found in the provided list".to_owned(), - )); - } - - Ok(selected_suites) -} - -/// Build a TLS client configuration with optional custom cipher suites and -/// certificate verifier This is the core function that handles TLS -/// configuration for all scenarios -fn build_tls_config_with_verifier( - cipher_suites: Option<&[SupportedCipherSuite]>, - verifier: Option>, - root_cert_store: Option, -) -> HttpClientResult { - let config_builder = match cipher_suites { - Some(suites) => rustls::ClientConfig::builder_with_provider( - rustls::crypto::CryptoProvider { - cipher_suites: suites.to_vec(), - ..rustls::crypto::aws_lc_rs::default_provider() - } - .into(), - ) - .with_safe_default_protocol_versions() - .map_err(|e| HttpClientError::Default(format!("TLS config error: {e}")))?, - None => rustls::ClientConfig::builder(), - }; - let final_config = match (verifier, root_cert_store, cipher_suites) { - // Custom verifier provided (TEE scenario) - (Some(custom_verifier), _, Some(_)) => { - // With custom cipher suites - config_builder - .dangerous() - .with_custom_certificate_verifier(custom_verifier) - .with_no_client_auth() - } - (Some(custom_verifier), _, None) => { - // No custom cipher suites - rustls::ClientConfig::builder() - .dangerous() - .with_custom_certificate_verifier(custom_verifier) - .with_no_client_auth() - } - // Root certificate store provided (standard TLS) - (None, Some(store), Some(_)) => { - // With custom cipher suites - need to use dangerous() first, then set roots - // manually Install default crypto provider if not already set - if let Err(e) = rustls::crypto::aws_lc_rs::default_provider().install_default() { - tracing::debug!("Crypto provider already installed: {e:?}"); - } - let verifier = rustls::client::WebPkiServerVerifier::builder(Arc::new(store)) - .build() - .map_err(|e| { - HttpClientError::Default(format!("Failed to build WebPkiServerVerifier: {e}")) - })?; - config_builder - .dangerous() - .with_custom_certificate_verifier(verifier) - .with_no_client_auth() - } - (None, Some(store), None) => { - // No custom cipher suites - rustls::ClientConfig::builder() - .with_root_certificates(store) - .with_no_client_auth() - } - // No verification (accept invalid certs) - (None, None, _) => { - let no_verifier = Arc::new(NoVerifier::new()); - if cipher_suites.is_some() { - config_builder - .dangerous() - .with_custom_certificate_verifier(no_verifier) - .with_no_client_auth() - } else { - rustls::ClientConfig::builder() - .dangerous() - .with_custom_certificate_verifier(no_verifier) - .with_no_client_auth() - } - } - }; - - Ok(final_config) -} - -/// Build a TLS client configuration with optional custom cipher suites -/// This is a convenience wrapper around `build_tls_config_with_verifier` for -/// standard TLS scenarios -fn build_tls_config( - cipher_suites: Option<&[SupportedCipherSuite]>, - accept_invalid_certs: bool, -) -> HttpClientResult { - if accept_invalid_certs { - // No verification needed - build_tls_config_with_verifier(cipher_suites, None, None) - } else { - // Standard TLS with root certificate verification - let mut root_cert_store = RootCertStore::empty(); - root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); - build_tls_config_with_verifier(cipher_suites, None, Some(root_cert_store)) - } -} - -/// Build a `TLSClient` to use with a server running inside a tee. -/// The TLS verification is the basic one but also includes the verification of -/// the leaf certificate The TLS socket is mounted since the leaf certificate is -/// exactly the same as the expected one. -fn build_tls_client_tee( - leaf_cert: CertificateDer<'static>, - accept_invalid_certs: bool, - cipher_suites: Option<&str>, -) -> ClientBuilder { - // Install default crypto provider if not already set - if let Err(e) = rustls::crypto::aws_lc_rs::default_provider().install_default() { - tracing::debug!("Crypto provider already installed: {e:?}"); - } - - let mut root_cert_store = RootCertStore::empty(); - root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); - - let default_verifier: Arc = if accept_invalid_certs { - Arc::new(NoVerifier::new()) - } else { - match rustls::client::WebPkiServerVerifier::builder(Arc::new(root_cert_store.clone())) - .build() - { - Ok(verifier) => verifier, - Err(e) => { - tracing::error!("Failed to build WebPkiServerVerifier: {e}, using NoVerifier"); - Arc::new(NoVerifier::new()) - } - } - }; - - let verifier = Arc::new(LeafCertificateVerifier::new(leaf_cert, default_verifier)); - - let cipher_suites_vec = cipher_suites.and_then(|cs| parse_cipher_suites(cs).ok()); - - let config = match build_tls_config_with_verifier( - cipher_suites_vec.as_deref(), - Some(verifier.clone()), - None, - ) { - Ok(config) => config, - Err(e) => { - tracing::error!("TLS config error: {e}, falling back to safe defaults"); - rustls::ClientConfig::builder() - .dangerous() - .with_custom_certificate_verifier(verifier) - .with_no_client_auth() - } - }; - - // Create a client builder - Client::builder().use_preconfigured_tls(config) -} diff --git a/crate/logger/README.md b/crate/logger/README.md index dd7c7a3..ba4ca1c 100644 --- a/crate/logger/README.md +++ b/crate/logger/README.md @@ -11,7 +11,7 @@ A flexible logging crate that supports both synchronous and asynchronous environ ```toml [dependencies] -cosmian_logger = { version = "0.6.0", features = ["full"] } +cosmian_logger = { version = "0.7.0", features = ["full"] } ``` ## Usage @@ -22,7 +22,7 @@ For applications that need OpenTelemetry and advanced features: ```toml [dependencies] -cosmian_logger = { version = "0.6.0", features = ["full"] } +cosmian_logger = { version = "0.7.0", features = ["full"] } ``` ```rust @@ -55,7 +55,7 @@ For synchronous applications that only need basic logging: ```toml [dependencies] -cosmian_logger = "0.6.0" +cosmian_logger = "0.7.0" ``` ```rust @@ -142,7 +142,7 @@ For more advanced use cases with OpenTelemetry integration, enable the `full` fe ```toml [dependencies] -cosmian_logger = { version = "0.6.0", features = ["full"] } +cosmian_logger = { version = "0.7.0", features = ["full"] } ``` ```rust diff --git a/crate/logger/src/lib.rs b/crate/logger/src/lib.rs index 03956cd..91cddd2 100644 --- a/crate/logger/src/lib.rs +++ b/crate/logger/src/lib.rs @@ -17,7 +17,7 @@ //! //! ```toml //! [dependencies] -//! cosmian_logger = { version = "0.6.0", features = ["full"] } +//! cosmian_logger = { version = "0.7.0", features = ["full"] } //! ``` //! //! If you get an error like "no `TelemetryConfig` in the root", it means you From f0ab37f5236176619bd3a2b3d2ab29500348f53a Mon Sep 17 00:00:00 2001 From: Manuthor Date: Tue, 23 Dec 2025 08:57:55 +0100 Subject: [PATCH 2/3] fix: uniformize toolchain versions --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 30dbc91..7e799aa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: cargo-fmt: uses: Cosmian/reusable_workflows/.github/workflows/cargo-fmt.yml@develop with: - toolchain: nightly-2025-03-31 + toolchain: 1.90.0 cargo-audit: uses: Cosmian/reusable_workflows/.github/workflows/cargo-audit.yml@develop with: @@ -52,7 +52,7 @@ jobs: cargo-semver: uses: Cosmian/reusable_workflows/.github/workflows/cargo-semver.yml@develop with: - toolchain: 1.88.0 + toolchain: 1.90.0 cargo-publish-workspace: uses: Cosmian/reusable_workflows/.github/workflows/cargo-publish-workspace.yml@develop with: From 111f4baeb1acb6d151aaa2275866da9eefb2647a Mon Sep 17 00:00:00 2001 From: Manuthor Date: Tue, 23 Dec 2025 09:02:15 +0100 Subject: [PATCH 3/3] fix: remove duplicated cargo-deny actions --- .github/workflows/ci.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e799aa..7cf42a0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,13 +6,6 @@ on: workflow_call: jobs: - audit: - name: Security Audit - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v4 - - uses: EmbarkStudios/cargo-deny-action@v1 - multi-os: strategy: matrix: