From c98654fc37f2752bcb751f5d96b181e985720f97 Mon Sep 17 00:00:00 2001 From: gabrik Date: Thu, 4 Apr 2024 10:23:50 +0200 Subject: [PATCH 1/6] refactor(tls-quic): moving shared code into zenoh-link-commons::tls Signed-off-by: gabrik --- io/zenoh-link-commons/Cargo.toml | 2 + io/zenoh-link-commons/src/tls.rs | 150 ++++++++++++++++++ io/zenoh-link/src/lib.rs | 13 +- io/zenoh-links/zenoh-link-quic/src/lib.rs | 103 +----------- io/zenoh-links/zenoh-link-quic/src/unicast.rs | 12 +- io/zenoh-links/zenoh-link-tls/src/lib.rs | 150 +----------------- io/zenoh-links/zenoh-link-tls/src/unicast.rs | 16 +- io/zenoh-transport/Cargo.toml | 1 + io/zenoh-transport/tests/endpoints.rs | 4 +- .../tests/unicast_authenticator.rs | 4 +- io/zenoh-transport/tests/unicast_multilink.rs | 4 +- io/zenoh-transport/tests/unicast_openclose.rs | 4 +- io/zenoh-transport/tests/unicast_transport.rs | 10 +- 13 files changed, 199 insertions(+), 274 deletions(-) diff --git a/io/zenoh-link-commons/Cargo.toml b/io/zenoh-link-commons/Cargo.toml index f2e10616c1..dd045003e4 100644 --- a/io/zenoh-link-commons/Cargo.toml +++ b/io/zenoh-link-commons/Cargo.toml @@ -34,9 +34,11 @@ rustls-webpki = { workspace = true } flume = { workspace = true } tracing = {workspace = true} serde = { workspace = true, features = ["default"] } +secrecy = {workspace = true } zenoh-buffers = { workspace = true } zenoh-codec = { workspace = true } zenoh-core = { workspace = true } +zenoh-config = { workspace = true } zenoh-protocol = { workspace = true } zenoh-result = { workspace = true } zenoh-util = { workspace = true } diff --git a/io/zenoh-link-commons/src/tls.rs b/io/zenoh-link-commons/src/tls.rs index 562b02c81e..4b6a723569 100644 --- a/io/zenoh-link-commons/src/tls.rs +++ b/io/zenoh-link-commons/src/tls.rs @@ -11,6 +11,19 @@ use rustls::{ }; use webpki::ALL_VERIFICATION_ALGS; +use crate::ConfigurationInspector; +use secrecy::ExposeSecret; +use zenoh_config::Config; +use zenoh_protocol::core::endpoint; +use zenoh_result::{bail, ZResult}; + +use config::{ + TLS_CLIENT_AUTH, TLS_CLIENT_CERTIFICATE_BASE64, TLS_CLIENT_CERTIFICATE_FILE, + TLS_CLIENT_PRIVATE_KEY_BASE64, TLS_CLIENT_PRIVATE_KEY_FILE, TLS_ROOT_CA_CERTIFICATE_BASE64, + TLS_ROOT_CA_CERTIFICATE_FILE, TLS_SERVER_CERTIFICATE_BASE64, TLS_SERVER_CERTIFICATE_FILE, + TLS_SERVER_NAME_VERIFICATION, TLS_SERVER_PRIVATE_KEY_BASE_64, TLS_SERVER_PRIVATE_KEY_FILE, +}; + impl ServerCertVerifier for WebPkiVerifierAnyServerName { /// Will verify the certificate is valid in the following ways: /// - Signed by a trusted `RootCertStore` CA @@ -85,3 +98,140 @@ impl WebPkiVerifierAnyServerName { Self { roots } } } + +pub mod config { + pub const TLS_ROOT_CA_CERTIFICATE_FILE: &str = "root_ca_certificate_file"; + pub const TLS_ROOT_CA_CERTIFICATE_RAW: &str = "root_ca_certificate_raw"; + pub const TLS_ROOT_CA_CERTIFICATE_BASE64: &str = "root_ca_certificate_base64"; + + pub const TLS_SERVER_PRIVATE_KEY_FILE: &str = "server_private_key_file"; + pub const TLS_SERVER_PRIVATE_KEY_RAW: &str = "server_private_key_raw"; + pub const TLS_SERVER_PRIVATE_KEY_BASE_64: &str = "server_private_key_base64"; + + pub const TLS_SERVER_CERTIFICATE_FILE: &str = "server_certificate_file"; + pub const TLS_SERVER_CERTIFICATE_RAW: &str = "server_certificate_raw"; + pub const TLS_SERVER_CERTIFICATE_BASE64: &str = "server_certificate_base64"; + + pub const TLS_CLIENT_PRIVATE_KEY_FILE: &str = "client_private_key_file"; + pub const TLS_CLIENT_PRIVATE_KEY_RAW: &str = "client_private_key_raw"; + pub const TLS_CLIENT_PRIVATE_KEY_BASE64: &str = "client_private_key_base64"; + + pub const TLS_CLIENT_CERTIFICATE_FILE: &str = "client_certificate_file"; + pub const TLS_CLIENT_CERTIFICATE_RAW: &str = "client_certificate_raw"; + pub const TLS_CLIENT_CERTIFICATE_BASE64: &str = "client_certificate_base64"; + + pub const TLS_CLIENT_AUTH: &str = "client_auth"; + + pub const TLS_SERVER_NAME_VERIFICATION: &str = "server_name_verification"; + pub const TLS_SERVER_NAME_VERIFICATION_DEFAULT: &str = "true"; +} + +#[derive(Default, Clone, Copy, Debug)] +pub struct TlsConfigurator; + +impl ConfigurationInspector for TlsConfigurator { + fn inspect_config(&self, config: &Config) -> ZResult { + let mut ps: Vec<(&str, &str)> = vec![]; + + let c = config.transport().link().tls(); + + match (c.root_ca_certificate(), c.root_ca_certificate_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'root_ca_certificate' and 'root_ca_certificate_base64' can be present!") + } + (Some(ca_certificate), None) => { + ps.push((TLS_ROOT_CA_CERTIFICATE_FILE, ca_certificate)); + } + (None, Some(ca_certificate)) => { + ps.push(( + TLS_ROOT_CA_CERTIFICATE_BASE64, + ca_certificate.expose_secret(), + )); + } + _ => {} + } + + match (c.server_private_key(), c.server_private_key_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'server_private_key' and 'server_private_key_base64' can be present!") + } + (Some(server_private_key), None) => { + ps.push((TLS_SERVER_PRIVATE_KEY_FILE, server_private_key)); + } + (None, Some(server_private_key)) => { + ps.push(( + TLS_SERVER_PRIVATE_KEY_BASE_64, + server_private_key.expose_secret(), + )); + } + _ => {} + } + + match (c.server_certificate(), c.server_certificate_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'server_certificate' and 'server_certificate_base64' can be present!") + } + (Some(server_certificate), None) => { + ps.push((TLS_SERVER_CERTIFICATE_FILE, server_certificate)); + } + (None, Some(server_certificate)) => { + ps.push(( + TLS_SERVER_CERTIFICATE_BASE64, + server_certificate.expose_secret(), + )); + } + _ => {} + } + + if let Some(client_auth) = c.client_auth() { + match client_auth { + true => ps.push((TLS_CLIENT_AUTH, "true")), + false => ps.push((TLS_CLIENT_AUTH, "false")), + }; + } + + match (c.client_private_key(), c.client_private_key_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'client_private_key' and 'client_private_key_base64' can be present!") + } + (Some(client_private_key), None) => { + ps.push((TLS_CLIENT_PRIVATE_KEY_FILE, client_private_key)); + } + (None, Some(client_private_key)) => { + ps.push(( + TLS_CLIENT_PRIVATE_KEY_BASE64, + client_private_key.expose_secret(), + )); + } + _ => {} + } + + match (c.client_certificate(), c.client_certificate_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'client_certificate' and 'client_certificate_base64' can be present!") + } + (Some(client_certificate), None) => { + ps.push((TLS_CLIENT_CERTIFICATE_FILE, client_certificate)); + } + (None, Some(client_certificate)) => { + ps.push(( + TLS_CLIENT_CERTIFICATE_BASE64, + client_certificate.expose_secret(), + )); + } + _ => {} + } + + if let Some(server_name_verification) = c.server_name_verification() { + match server_name_verification { + true => ps.push((TLS_SERVER_NAME_VERIFICATION, "true")), + false => ps.push((TLS_SERVER_NAME_VERIFICATION, "false")), + }; + } + + let mut s = String::new(); + endpoint::Parameters::extend(ps.drain(..), &mut s); + + Ok(s) + } +} diff --git a/io/zenoh-link/src/lib.rs b/io/zenoh-link/src/lib.rs index 21f26ecf1b..a37eabaef8 100644 --- a/io/zenoh-link/src/lib.rs +++ b/io/zenoh-link/src/lib.rs @@ -19,6 +19,9 @@ //! [Click here for Zenoh's documentation](../zenoh/index.html) use std::collections::HashMap; use zenoh_config::Config; + +#[cfg(any(feature = "transport_quic", feature = "transport_tls"))] +use zenoh_link_commons::tls::TlsConfigurator; use zenoh_result::{bail, ZResult}; #[cfg(feature = "transport_tcp")] @@ -36,16 +39,12 @@ use zenoh_link_udp::{ #[cfg(feature = "transport_tls")] pub use zenoh_link_tls as tls; #[cfg(feature = "transport_tls")] -use zenoh_link_tls::{ - LinkManagerUnicastTls, TlsConfigurator, TlsLocatorInspector, TLS_LOCATOR_PREFIX, -}; +use zenoh_link_tls::{LinkManagerUnicastTls, TlsLocatorInspector, TLS_LOCATOR_PREFIX}; #[cfg(feature = "transport_quic")] pub use zenoh_link_quic as quic; #[cfg(feature = "transport_quic")] -use zenoh_link_quic::{ - LinkManagerUnicastQuic, QuicConfigurator, QuicLocatorInspector, QUIC_LOCATOR_PREFIX, -}; +use zenoh_link_quic::{LinkManagerUnicastQuic, QuicLocatorInspector, QUIC_LOCATOR_PREFIX}; #[cfg(feature = "transport_ws")] pub use zenoh_link_ws as ws; @@ -155,7 +154,7 @@ impl LocatorInspector { #[derive(Default)] pub struct LinkConfigurator { #[cfg(feature = "transport_quic")] - quic_inspector: QuicConfigurator, + quic_inspector: TlsConfigurator, #[cfg(feature = "transport_tls")] tls_inspector: TlsConfigurator, #[cfg(feature = "transport_unixpipe")] diff --git a/io/zenoh-links/zenoh-link-quic/src/lib.rs b/io/zenoh-links/zenoh-link-quic/src/lib.rs index c6d7e16087..113f11ac78 100644 --- a/io/zenoh-links/zenoh-link-quic/src/lib.rs +++ b/io/zenoh-links/zenoh-link-quic/src/lib.rs @@ -18,20 +18,11 @@ //! //! [Click here for Zenoh's documentation](../zenoh/index.html) use async_trait::async_trait; -use config::{ - TLS_ROOT_CA_CERTIFICATE_BASE64, TLS_ROOT_CA_CERTIFICATE_FILE, TLS_SERVER_CERTIFICATE_BASE64, - TLS_SERVER_CERTIFICATE_FILE, TLS_SERVER_NAME_VERIFICATION, TLS_SERVER_PRIVATE_KEY_BASE64, - TLS_SERVER_PRIVATE_KEY_FILE, -}; -use secrecy::ExposeSecret; + use std::net::SocketAddr; -use zenoh_config::Config; use zenoh_core::zconfigurable; -use zenoh_link_commons::{ConfigurationInspector, LocatorInspector}; -use zenoh_protocol::core::{ - endpoint::{Address, Parameters}, - Locator, -}; +use zenoh_link_commons::LocatorInspector; +use zenoh_protocol::core::{endpoint::Address, Locator}; use zenoh_result::{bail, zerror, ZResult}; mod unicast; @@ -64,77 +55,6 @@ impl LocatorInspector for QuicLocatorInspector { } } -#[derive(Default, Clone, Copy, Debug)] -pub struct QuicConfigurator; - -impl ConfigurationInspector for QuicConfigurator { - fn inspect_config(&self, config: &Config) -> ZResult { - let mut ps: Vec<(&str, &str)> = vec![]; - - let c = config.transport().link().tls(); - - match (c.root_ca_certificate(), c.root_ca_certificate_base64()) { - (Some(_), Some(_)) => { - bail!("Only one between 'root_ca_certificate' and 'root_ca_certificate_base64' can be present!") - } - (Some(ca_certificate), None) => { - ps.push((TLS_ROOT_CA_CERTIFICATE_FILE, ca_certificate)); - } - (None, Some(ca_certificate)) => { - ps.push(( - TLS_ROOT_CA_CERTIFICATE_BASE64, - ca_certificate.expose_secret(), - )); - } - _ => {} - } - - match (c.server_private_key(), c.server_private_key_base64()) { - (Some(_), Some(_)) => { - bail!("Only one between 'server_private_key' and 'server_private_key_base64' can be present!") - } - (Some(server_private_key), None) => { - ps.push((TLS_SERVER_PRIVATE_KEY_FILE, server_private_key)); - } - (None, Some(server_private_key)) => { - ps.push(( - TLS_SERVER_PRIVATE_KEY_BASE64, - server_private_key.expose_secret(), - )); - } - _ => {} - } - - match (c.server_certificate(), c.server_certificate_base64()) { - (Some(_), Some(_)) => { - bail!("Only one between 'server_certificate' and 'server_certificate_base64' can be present!") - } - (Some(server_certificate), None) => { - ps.push((TLS_SERVER_CERTIFICATE_FILE, server_certificate)); - } - (None, Some(server_certificate)) => { - ps.push(( - TLS_SERVER_CERTIFICATE_BASE64, - server_certificate.expose_secret(), - )); - } - _ => {} - } - - if let Some(server_name_verification) = c.server_name_verification() { - match server_name_verification { - true => ps.push((TLS_SERVER_NAME_VERIFICATION, "true")), - false => ps.push((TLS_SERVER_NAME_VERIFICATION, "false")), - }; - } - - let mut s = String::new(); - Parameters::extend(ps.drain(..), &mut s); - - Ok(s) - } -} - zconfigurable! { // Default MTU (QUIC PDU) in bytes. static ref QUIC_DEFAULT_MTU: u16 = QUIC_MAX_MTU; @@ -148,23 +68,6 @@ zconfigurable! { static ref QUIC_ACCEPT_THROTTLE_TIME: u64 = 100_000; } -pub mod config { - pub const TLS_ROOT_CA_CERTIFICATE_FILE: &str = "root_ca_certificate_file"; - pub const TLS_ROOT_CA_CERTIFICATE_RAW: &str = "root_ca_certificate_raw"; - pub const TLS_ROOT_CA_CERTIFICATE_BASE64: &str = "root_ca_certificate_base64"; - - pub const TLS_SERVER_PRIVATE_KEY_FILE: &str = "server_private_key_file"; - pub const TLS_SERVER_PRIVATE_KEY_RAW: &str = "server_private_key_raw"; - pub const TLS_SERVER_PRIVATE_KEY_BASE64: &str = "server_private_key_base64"; - - pub const TLS_SERVER_CERTIFICATE_FILE: &str = "tls_server_certificate_file"; - pub const TLS_SERVER_CERTIFICATE_RAW: &str = "tls_server_certificate_raw"; - pub const TLS_SERVER_CERTIFICATE_BASE64: &str = "tls_server_certificate_base64"; - - pub const TLS_SERVER_NAME_VERIFICATION: &str = "server_name_verification"; - pub const TLS_SERVER_NAME_VERIFICATION_DEFAULT: &str = "true"; -} - async fn get_quic_addr(address: &Address<'_>) -> ZResult { match tokio::net::lookup_host(address.as_str()).await?.next() { Some(addr) => Ok(addr), diff --git a/io/zenoh-links/zenoh-link-quic/src/unicast.rs b/io/zenoh-links/zenoh-link-quic/src/unicast.rs index 8fd7777137..4c10315e61 100644 --- a/io/zenoh-links/zenoh-link-quic/src/unicast.rs +++ b/io/zenoh-links/zenoh-link-quic/src/unicast.rs @@ -14,8 +14,8 @@ use crate::base64_decode; use crate::{ - config::*, get_quic_addr, verify::WebPkiVerifierAnyServerName, ALPN_QUIC_HTTP, - QUIC_ACCEPT_THROTTLE_TIME, QUIC_DEFAULT_MTU, QUIC_LOCATOR_PREFIX, + get_quic_addr, verify::WebPkiVerifierAnyServerName, ALPN_QUIC_HTTP, QUIC_ACCEPT_THROTTLE_TIME, + QUIC_DEFAULT_MTU, QUIC_LOCATOR_PREFIX, }; use async_trait::async_trait; use rustls::{Certificate, PrivateKey}; @@ -29,6 +29,12 @@ use std::time::Duration; use tokio::sync::Mutex as AsyncMutex; use tokio_util::sync::CancellationToken; use zenoh_core::zasynclock; +use zenoh_link_commons::tls::config::{ + TLS_ROOT_CA_CERTIFICATE_BASE64, TLS_ROOT_CA_CERTIFICATE_FILE, TLS_ROOT_CA_CERTIFICATE_RAW, + TLS_SERVER_CERTIFICATE_BASE64, TLS_SERVER_CERTIFICATE_FILE, TLS_SERVER_CERTIFICATE_RAW, + TLS_SERVER_NAME_VERIFICATION, TLS_SERVER_NAME_VERIFICATION_DEFAULT, + TLS_SERVER_PRIVATE_KEY_FILE, TLS_SERVER_PRIVATE_KEY_RAW, +}; use zenoh_link_commons::{ get_ip_interface_names, LinkManagerUnicastTrait, LinkUnicast, LinkUnicastTrait, ListenersUnicastIP, NewLinkChannelSender, @@ -336,7 +342,7 @@ impl LinkManagerUnicastTrait for LinkManagerUnicastQuic { // Private keys let f = if let Some(value) = epconf.get(TLS_SERVER_PRIVATE_KEY_RAW) { value.as_bytes().to_vec() - } else if let Some(b64_key) = epconf.get(TLS_SERVER_PRIVATE_KEY_BASE64) { + } else if let Some(b64_key) = epconf.get(TLS_SERVER_PRIVATE_KEY_RAW) { base64_decode(b64_key)? } else if let Some(value) = epconf.get(TLS_SERVER_PRIVATE_KEY_FILE) { tokio::fs::read(value) diff --git a/io/zenoh-links/zenoh-link-tls/src/lib.rs b/io/zenoh-links/zenoh-link-tls/src/lib.rs index 95d59104b4..460bea400b 100644 --- a/io/zenoh-links/zenoh-link-tls/src/lib.rs +++ b/io/zenoh-links/zenoh-link-tls/src/lib.rs @@ -18,22 +18,11 @@ //! //! [Click here for Zenoh's documentation](../zenoh/index.html) use async_trait::async_trait; -use config::{ - TLS_CLIENT_AUTH, TLS_CLIENT_CERTIFICATE_BASE64, TLS_CLIENT_CERTIFICATE_FILE, - TLS_CLIENT_PRIVATE_KEY_BASE64, TLS_CLIENT_PRIVATE_KEY_FILE, TLS_ROOT_CA_CERTIFICATE_BASE64, - TLS_ROOT_CA_CERTIFICATE_FILE, TLS_SERVER_CERTIFICATE_BASE64, TLS_SERVER_CERTIFICATE_FILE, - TLS_SERVER_NAME_VERIFICATION, TLS_SERVER_PRIVATE_KEY_BASE_64, TLS_SERVER_PRIVATE_KEY_FILE, -}; use rustls_pki_types::ServerName; -use secrecy::ExposeSecret; use std::{convert::TryFrom, net::SocketAddr}; -use zenoh_config::Config; use zenoh_core::zconfigurable; -use zenoh_link_commons::{ConfigurationInspector, LocatorInspector}; -use zenoh_protocol::core::{ - endpoint::{self, Address}, - Locator, -}; +use zenoh_link_commons::LocatorInspector; +use zenoh_protocol::core::{endpoint::Address, Locator}; use zenoh_result::{bail, zerror, ZResult}; mod unicast; @@ -60,115 +49,6 @@ impl LocatorInspector for TlsLocatorInspector { Ok(false) } } -#[derive(Default, Clone, Copy, Debug)] -pub struct TlsConfigurator; - -impl ConfigurationInspector for TlsConfigurator { - fn inspect_config(&self, config: &Config) -> ZResult { - let mut ps: Vec<(&str, &str)> = vec![]; - - let c = config.transport().link().tls(); - - match (c.root_ca_certificate(), c.root_ca_certificate_base64()) { - (Some(_), Some(_)) => { - bail!("Only one between 'root_ca_certificate' and 'root_ca_certificate_base64' can be present!") - } - (Some(ca_certificate), None) => { - ps.push((TLS_ROOT_CA_CERTIFICATE_FILE, ca_certificate)); - } - (None, Some(ca_certificate)) => { - ps.push(( - TLS_ROOT_CA_CERTIFICATE_BASE64, - ca_certificate.expose_secret(), - )); - } - _ => {} - } - - match (c.server_private_key(), c.server_private_key_base64()) { - (Some(_), Some(_)) => { - bail!("Only one between 'server_private_key' and 'server_private_key_base64' can be present!") - } - (Some(server_private_key), None) => { - ps.push((TLS_SERVER_PRIVATE_KEY_FILE, server_private_key)); - } - (None, Some(server_private_key)) => { - ps.push(( - TLS_SERVER_PRIVATE_KEY_BASE_64, - server_private_key.expose_secret(), - )); - } - _ => {} - } - - match (c.server_certificate(), c.server_certificate_base64()) { - (Some(_), Some(_)) => { - bail!("Only one between 'server_certificate' and 'server_certificate_base64' can be present!") - } - (Some(server_certificate), None) => { - ps.push((TLS_SERVER_CERTIFICATE_FILE, server_certificate)); - } - (None, Some(server_certificate)) => { - ps.push(( - TLS_SERVER_CERTIFICATE_BASE64, - server_certificate.expose_secret(), - )); - } - _ => {} - } - - if let Some(client_auth) = c.client_auth() { - match client_auth { - true => ps.push((TLS_CLIENT_AUTH, "true")), - false => ps.push((TLS_CLIENT_AUTH, "false")), - }; - } - - match (c.client_private_key(), c.client_private_key_base64()) { - (Some(_), Some(_)) => { - bail!("Only one between 'client_private_key' and 'client_private_key_base64' can be present!") - } - (Some(client_private_key), None) => { - ps.push((TLS_CLIENT_PRIVATE_KEY_FILE, client_private_key)); - } - (None, Some(client_private_key)) => { - ps.push(( - TLS_CLIENT_PRIVATE_KEY_BASE64, - client_private_key.expose_secret(), - )); - } - _ => {} - } - - match (c.client_certificate(), c.client_certificate_base64()) { - (Some(_), Some(_)) => { - bail!("Only one between 'client_certificate' and 'client_certificate_base64' can be present!") - } - (Some(client_certificate), None) => { - ps.push((TLS_CLIENT_CERTIFICATE_FILE, client_certificate)); - } - (None, Some(client_certificate)) => { - ps.push(( - TLS_CLIENT_CERTIFICATE_BASE64, - client_certificate.expose_secret(), - )); - } - _ => {} - } - - if let Some(server_name_verification) = c.server_name_verification() { - match server_name_verification { - true => ps.push((TLS_SERVER_NAME_VERIFICATION, "true")), - false => ps.push((TLS_SERVER_NAME_VERIFICATION, "false")), - }; - } - - let mut s = String::new(); - endpoint::Parameters::extend(ps.drain(..), &mut s); - - Ok(s) - } -} zconfigurable! { // Default MTU (TLS PDU) in bytes. @@ -183,32 +63,6 @@ zconfigurable! { static ref TLS_ACCEPT_THROTTLE_TIME: u64 = 100_000; } -pub mod config { - pub const TLS_ROOT_CA_CERTIFICATE_FILE: &str = "root_ca_certificate_file"; - pub const TLS_ROOT_CA_CERTIFICATE_RAW: &str = "root_ca_certificate_raw"; - pub const TLS_ROOT_CA_CERTIFICATE_BASE64: &str = "root_ca_certificate_base64"; - - pub const TLS_SERVER_PRIVATE_KEY_FILE: &str = "server_private_key_file"; - pub const TLS_SERVER_PRIVATE_KEY_RAW: &str = "server_private_key_raw"; - pub const TLS_SERVER_PRIVATE_KEY_BASE_64: &str = "server_private_key_base64"; - - pub const TLS_SERVER_CERTIFICATE_FILE: &str = "server_certificate_file"; - pub const TLS_SERVER_CERTIFICATE_RAW: &str = "server_certificate_raw"; - pub const TLS_SERVER_CERTIFICATE_BASE64: &str = "server_certificate_base64"; - - pub const TLS_CLIENT_PRIVATE_KEY_FILE: &str = "client_private_key_file"; - pub const TLS_CLIENT_PRIVATE_KEY_RAW: &str = "client_private_key_raw"; - pub const TLS_CLIENT_PRIVATE_KEY_BASE64: &str = "client_private_key_base64"; - - pub const TLS_CLIENT_CERTIFICATE_FILE: &str = "client_certificate_file"; - pub const TLS_CLIENT_CERTIFICATE_RAW: &str = "client_certificate_raw"; - pub const TLS_CLIENT_CERTIFICATE_BASE64: &str = "client_certificate_base64"; - - pub const TLS_CLIENT_AUTH: &str = "client_auth"; - - pub const TLS_SERVER_NAME_VERIFICATION: &str = "server_name_verification"; -} - pub async fn get_tls_addr(address: &Address<'_>) -> ZResult { match tokio::net::lookup_host(address.as_str()).await?.next() { Some(addr) => Ok(addr), diff --git a/io/zenoh-links/zenoh-link-tls/src/unicast.rs b/io/zenoh-links/zenoh-link-tls/src/unicast.rs index 9eec2feb2a..5e7eb78e22 100644 --- a/io/zenoh-links/zenoh-link-tls/src/unicast.rs +++ b/io/zenoh-links/zenoh-link-tls/src/unicast.rs @@ -12,8 +12,8 @@ // ZettaScale Zenoh Team, // use crate::{ - base64_decode, config::*, get_tls_addr, get_tls_host, get_tls_server_name, - TLS_ACCEPT_THROTTLE_TIME, TLS_DEFAULT_MTU, TLS_LINGER_TIMEOUT, TLS_LOCATOR_PREFIX, + base64_decode, get_tls_addr, get_tls_host, get_tls_server_name, TLS_ACCEPT_THROTTLE_TIME, + TLS_DEFAULT_MTU, TLS_LINGER_TIMEOUT, TLS_LOCATOR_PREFIX, }; use async_trait::async_trait; use rustls::{ @@ -37,7 +37,17 @@ use tokio_rustls::{TlsAcceptor, TlsConnector, TlsStream}; use tokio_util::sync::CancellationToken; use webpki::anchor_from_trusted_cert; use zenoh_core::zasynclock; -use zenoh_link_commons::tls::WebPkiVerifierAnyServerName; +use zenoh_link_commons::tls::{ + config::{ + TLS_CLIENT_AUTH, TLS_CLIENT_CERTIFICATE_BASE64, TLS_CLIENT_CERTIFICATE_FILE, + TLS_CLIENT_CERTIFICATE_RAW, TLS_CLIENT_PRIVATE_KEY_BASE64, TLS_CLIENT_PRIVATE_KEY_FILE, + TLS_CLIENT_PRIVATE_KEY_RAW, TLS_ROOT_CA_CERTIFICATE_BASE64, TLS_ROOT_CA_CERTIFICATE_FILE, + TLS_ROOT_CA_CERTIFICATE_RAW, TLS_SERVER_CERTIFICATE_BASE64, TLS_SERVER_CERTIFICATE_FILE, + TLS_SERVER_CERTIFICATE_RAW, TLS_SERVER_NAME_VERIFICATION, TLS_SERVER_PRIVATE_KEY_BASE_64, + TLS_SERVER_PRIVATE_KEY_FILE, TLS_SERVER_PRIVATE_KEY_RAW, + }, + WebPkiVerifierAnyServerName, +}; use zenoh_link_commons::{ get_ip_interface_names, LinkManagerUnicastTrait, LinkUnicast, LinkUnicastTrait, ListenersUnicastIP, NewLinkChannelSender, diff --git a/io/zenoh-transport/Cargo.toml b/io/zenoh-transport/Cargo.toml index b3a299e8be..9f6594761e 100644 --- a/io/zenoh-transport/Cargo.toml +++ b/io/zenoh-transport/Cargo.toml @@ -92,3 +92,4 @@ futures-util = { workspace = true } zenoh-util = {workspace = true } zenoh-protocol = { workspace = true, features = ["test"] } futures = { workspace = true } +zenoh-link-commons = { workspace = true } diff --git a/io/zenoh-transport/tests/endpoints.rs b/io/zenoh-transport/tests/endpoints.rs index 6269f78cb9..85e5e4bfef 100644 --- a/io/zenoh-transport/tests/endpoints.rs +++ b/io/zenoh-transport/tests/endpoints.rs @@ -255,7 +255,7 @@ async fn endpoint_udp_unix() { #[cfg(feature = "transport_tls")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn endpoint_tls() { - use zenoh_link::tls::config::*; + use zenoh_link_commons::tls::config::*; zenoh_util::try_init_log_from_env(); @@ -334,7 +334,7 @@ AXVFFIgCSluyrolaD6CWD9MqOex4YOfJR2bNxI7lFvuK4AwjyUJzT1U1HXib17mM #[cfg(feature = "transport_quic")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn endpoint_quic() { - use zenoh_link::quic::config::*; + use zenoh_link_commons::tls::config::*; zenoh_util::try_init_log_from_env(); diff --git a/io/zenoh-transport/tests/unicast_authenticator.rs b/io/zenoh-transport/tests/unicast_authenticator.rs index a232584cff..62b645123b 100644 --- a/io/zenoh-transport/tests/unicast_authenticator.rs +++ b/io/zenoh-transport/tests/unicast_authenticator.rs @@ -719,7 +719,7 @@ async fn authenticator_unix() { #[cfg(feature = "transport_tls")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn authenticator_tls() { - use zenoh_link::tls::config::*; + use zenoh_link_commons::tls::config::*; zenoh_util::try_init_log_from_env(); @@ -819,7 +819,7 @@ R+IdLiXcyIkg0m9N8I17p0ljCSkbrgGMD3bbePRTfg== #[cfg(feature = "transport_quic")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn authenticator_quic() { - use zenoh_link::quic::config::*; + use zenoh_link_commons::tls::config::*; zenoh_util::try_init_log_from_env(); diff --git a/io/zenoh-transport/tests/unicast_multilink.rs b/io/zenoh-transport/tests/unicast_multilink.rs index d69a30ac9d..6ae96a79b1 100644 --- a/io/zenoh-transport/tests/unicast_multilink.rs +++ b/io/zenoh-transport/tests/unicast_multilink.rs @@ -529,7 +529,7 @@ mod tests { #[cfg(feature = "transport_tls")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn multilink_tls_only() { - use zenoh_link::tls::config::*; + use zenoh_link_commons::tls::config::*; zenoh_util::try_init_log_from_env(); @@ -628,7 +628,7 @@ R+IdLiXcyIkg0m9N8I17p0ljCSkbrgGMD3bbePRTfg== #[cfg(feature = "transport_quic")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn multilink_quic_only() { - use zenoh_link::quic::config::*; + use zenoh_link_commons::tls::config::*; // NOTE: this an auto-generated pair of certificate and key. // The target domain is localhost, so it has no real diff --git a/io/zenoh-transport/tests/unicast_openclose.rs b/io/zenoh-transport/tests/unicast_openclose.rs index a671de14a8..d44c514d25 100644 --- a/io/zenoh-transport/tests/unicast_openclose.rs +++ b/io/zenoh-transport/tests/unicast_openclose.rs @@ -559,7 +559,7 @@ async fn openclose_unix_only() { #[cfg(feature = "transport_tls")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn openclose_tls_only() { - use zenoh_link::tls::config::*; + use zenoh_link_commons::tls::config::*; zenoh_util::try_init_log_from_env(); // NOTE: this an auto-generated pair of certificate and key. @@ -657,7 +657,7 @@ R+IdLiXcyIkg0m9N8I17p0ljCSkbrgGMD3bbePRTfg== #[cfg(feature = "transport_quic")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn openclose_quic_only() { - use zenoh_link::quic::config::*; + use zenoh_link_commons::tls::config::*; // NOTE: this an auto-generated pair of certificate and key. // The target domain is localhost, so it has no real diff --git a/io/zenoh-transport/tests/unicast_transport.rs b/io/zenoh-transport/tests/unicast_transport.rs index af1dedfbce..0c8ac25c74 100644 --- a/io/zenoh-transport/tests/unicast_transport.rs +++ b/io/zenoh-transport/tests/unicast_transport.rs @@ -991,7 +991,7 @@ async fn transport_unicast_tcp_udp_unix() { #[cfg(all(feature = "transport_tls", target_family = "unix"))] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn transport_unicast_tls_only_server() { - use zenoh_link::tls::config::*; + use zenoh_link_commons::tls::config::*; zenoh_util::try_init_log_from_env(); @@ -1037,7 +1037,7 @@ async fn transport_unicast_tls_only_server() { #[cfg(feature = "transport_quic")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn transport_unicast_quic_only_server() { - use zenoh_link::quic::config::*; + use zenoh_link_commons::tls::config::*; zenoh_util::try_init_log_from_env(); // Define the locator @@ -1082,7 +1082,7 @@ async fn transport_unicast_quic_only_server() { #[cfg(all(feature = "transport_tls", target_family = "unix"))] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn transport_unicast_tls_only_mutual_success() { - use zenoh_link::tls::config::*; + use zenoh_link_commons::tls::config::*; zenoh_util::try_init_log_from_env(); @@ -1154,7 +1154,7 @@ async fn transport_unicast_tls_only_mutual_success() { #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn transport_unicast_tls_only_mutual_no_client_certs_failure() { use std::vec; - use zenoh_link::tls::config::*; + use zenoh_link_commons::tls::config::*; zenoh_util::try_init_log_from_env(); @@ -1222,7 +1222,7 @@ async fn transport_unicast_tls_only_mutual_no_client_certs_failure() { #[cfg(all(feature = "transport_tls", target_family = "unix"))] #[test] fn transport_unicast_tls_only_mutual_wrong_client_certs_failure() { - use zenoh_link::tls::config::*; + use zenoh_link_commons::tls::config::*; zenoh_util::try_init_log_from_env(); From 6682595a9305ea9783a684b5e2a5536222c4dc27 Mon Sep 17 00:00:00 2001 From: gabrik Date: Thu, 4 Apr 2024 17:09:44 +0200 Subject: [PATCH 2/6] fix(mtls-quic): adding support for mTLS in QUIC [no ci] - broken Signed-off-by: gabrik --- Cargo.lock | 3 + Cargo.toml | 6 +- io/zenoh-link-commons/Cargo.toml | 42 ++- io/zenoh-link-commons/src/lib.rs | 2 + io/zenoh-link-commons/src/tls.rs | 344 +++++++++++++++++- io/zenoh-links/zenoh-link-quic/Cargo.toml | 6 +- io/zenoh-links/zenoh-link-quic/src/unicast.rs | 119 +----- io/zenoh-links/zenoh-link-tls/Cargo.toml | 3 +- io/zenoh-links/zenoh-link-tls/src/unicast.rs | 310 +--------------- 9 files changed, 388 insertions(+), 447 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 16f7b4d1a0..ed925977e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5113,12 +5113,14 @@ dependencies = [ "futures", "rustls 0.22.2", "rustls-webpki 0.102.2", + "secrecy", "serde", "tokio", "tokio-util", "tracing", "zenoh-buffers", "zenoh-codec", + "zenoh-config", "zenoh-core", "zenoh-protocol", "zenoh-result", @@ -5516,6 +5518,7 @@ dependencies = [ "zenoh-core", "zenoh-crypto", "zenoh-link", + "zenoh-link-commons", "zenoh-protocol", "zenoh-result", "zenoh-runtime", diff --git a/Cargo.toml b/Cargo.toml index dc24991488..115f11c119 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -122,7 +122,7 @@ petgraph = "0.6.3" pnet = "0.34" pnet_datalink = "0.34" proc-macro2 = "1.0.51" -quinn = "0.10.1" +quinn = {version = "0.10.1"} quote = "1.0.23" rand = { version = "0.8.5", default-features = false } # Default features are disabled due to usage in no_std crates rand_chacha = "0.3.1" @@ -132,7 +132,7 @@ ron = "0.8.1" ringbuffer-spsc = "0.1.9" rsa = "0.9" rustc_version = "0.4.0" -rustls = "0.22.2" +rustls = {version = "0.23", features = ["ring"]} rustls-native-certs = "0.7.0" rustls-pemfile = "2.0.0" rustls-webpki = "0.102.0" @@ -155,7 +155,7 @@ token-cell = { version = "1.4.2", default-features = false } tokio = { version = "1.35.1", default-features = false } # Default features are disabled due to some crates' requirements tokio-util = "0.7.10" tokio-tungstenite = "0.21" -tokio-rustls = "0.25.0" +tokio-rustls = "0.26.0" # tokio-vsock = see: io/zenoh-links/zenoh-link-vsock/Cargo.toml (workspaces does not support platform dependent dependencies) console-subscriber = "0.2" typenum = "1.16.0" diff --git a/io/zenoh-link-commons/Cargo.toml b/io/zenoh-link-commons/Cargo.toml index dd045003e4..9b169ce8d0 100644 --- a/io/zenoh-link-commons/Cargo.toml +++ b/io/zenoh-link-commons/Cargo.toml @@ -12,37 +12,49 @@ # ZettaScale Zenoh Team, # [package] -rust-version = { workspace = true } -name = "zenoh-link-commons" -version = { workspace = true } -repository = { workspace = true } -homepage = { workspace = true } authors = { workspace = true } -edition = { workspace = true } -license = { workspace = true } categories = { workspace = true } description = "Internal crate for zenoh." +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +name = "zenoh-link-commons" +repository = { workspace = true } +rust-version = { workspace = true } +version = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] compression = [] +tls = [ + "base64", + "rustls", + "rustls-native-certs", + "rustls-pemfile", + "rustls-webpki", + "webpki-roots", +] [dependencies] async-trait = { workspace = true } -rustls = { workspace = true } -rustls-webpki = { workspace = true } +base64 = { workspace = true, optional = true } flume = { workspace = true } tracing = {workspace = true} serde = { workspace = true, features = ["default"] } -secrecy = {workspace = true } +tokio = { workspace = true, features = [ + "fs", + "io-util", + "net", + "sync", + "time", +] } +tokio-util = { workspace = true, features = ["rt"] } +webpki-roots = { workspace = true, optional = true } zenoh-buffers = { workspace = true } zenoh-codec = { workspace = true } -zenoh-core = { workspace = true } zenoh-config = { workspace = true } +zenoh-core = { workspace = true } zenoh-protocol = { workspace = true } zenoh-result = { workspace = true } -zenoh-util = { workspace = true } zenoh-runtime = { workspace = true } -tokio = { workspace = true, features = ["io-util", "net", "fs", "sync", "time"] } -tokio-util = { workspace = true, features = ["rt"] } -futures = { workspace = true } +zenoh-util = { workspace = true } diff --git a/io/zenoh-link-commons/src/lib.rs b/io/zenoh-link-commons/src/lib.rs index f9ad7166ee..7a115530a9 100644 --- a/io/zenoh-link-commons/src/lib.rs +++ b/io/zenoh-link-commons/src/lib.rs @@ -21,7 +21,9 @@ extern crate alloc; mod listener; mod multicast; +#[cfg(feature = "tls")] pub mod tls; + mod unicast; use alloc::{borrow::ToOwned, boxed::Box, string::String, vec, vec::Vec}; diff --git a/io/zenoh-link-commons/src/tls.rs b/io/zenoh-link-commons/src/tls.rs index 4b6a723569..2f6051580f 100644 --- a/io/zenoh-link-commons/src/tls.rs +++ b/io/zenoh-link-commons/src/tls.rs @@ -1,3 +1,4 @@ +use crate::ConfigurationInspector; use alloc::vec::Vec; use rustls::{ client::{ @@ -5,25 +6,40 @@ use rustls::{ verify_server_cert_signed_by_trust_anchor, }, crypto::{verify_tls12_signature, verify_tls13_signature}, - pki_types::{CertificateDer, ServerName, UnixTime}, - server::ParsedCertificate, - RootCertStore, + pki_types::{CertificateDer, PrivateKeyDer, ServerName, TrustAnchor, UnixTime}, + server::{ParsedCertificate, WebPkiClientVerifier}, + version::TLS13, + ClientConfig, RootCertStore, ServerConfig, }; -use webpki::ALL_VERIFICATION_ALGS; - -use crate::ConfigurationInspector; use secrecy::ExposeSecret; +use std::{ + fs::File, + io::{BufRead, BufReader, Cursor}, + sync::Arc, +}; +use webpki::{anchor_from_trusted_cert, ALL_VERIFICATION_ALGS}; use zenoh_config::Config; +use zenoh_core::zerror; use zenoh_protocol::core::endpoint; -use zenoh_result::{bail, ZResult}; +use zenoh_result::{bail, ZError, ZResult}; use config::{ TLS_CLIENT_AUTH, TLS_CLIENT_CERTIFICATE_BASE64, TLS_CLIENT_CERTIFICATE_FILE, - TLS_CLIENT_PRIVATE_KEY_BASE64, TLS_CLIENT_PRIVATE_KEY_FILE, TLS_ROOT_CA_CERTIFICATE_BASE64, - TLS_ROOT_CA_CERTIFICATE_FILE, TLS_SERVER_CERTIFICATE_BASE64, TLS_SERVER_CERTIFICATE_FILE, - TLS_SERVER_NAME_VERIFICATION, TLS_SERVER_PRIVATE_KEY_BASE_64, TLS_SERVER_PRIVATE_KEY_FILE, + TLS_CLIENT_CERTIFICATE_RAW, TLS_CLIENT_PRIVATE_KEY_BASE64, TLS_CLIENT_PRIVATE_KEY_FILE, + TLS_CLIENT_PRIVATE_KEY_RAW, TLS_ROOT_CA_CERTIFICATE_BASE64, TLS_ROOT_CA_CERTIFICATE_FILE, + TLS_ROOT_CA_CERTIFICATE_RAW, TLS_SERVER_CERTIFICATE_BASE64, TLS_SERVER_CERTIFICATE_FILE, + TLS_SERVER_CERTIFICATE_RAW, TLS_SERVER_NAME_VERIFICATION, TLS_SERVER_PRIVATE_KEY_BASE_64, + TLS_SERVER_PRIVATE_KEY_FILE, TLS_SERVER_PRIVATE_KEY_RAW, }; +pub fn base64_decode(data: &str) -> ZResult> { + use base64::engine::general_purpose; + use base64::Engine; + Ok(general_purpose::STANDARD + .decode(data) + .map_err(|e| zerror!("Unable to perform base64 decoding: {e:?}"))?) +} + impl ServerCertVerifier for WebPkiVerifierAnyServerName { /// Will verify the certificate is valid in the following ways: /// - Signed by a trusted `RootCertStore` CA @@ -235,3 +251,311 @@ impl ConfigurationInspector for TlsConfigurator { Ok(s) } } + +pub struct TlsServerConfig { + pub server_config: ServerConfig, +} + +impl TlsServerConfig { + pub async fn new(config: &endpoint::Config<'_>) -> ZResult { + let tls_server_client_auth: bool = match config.get(TLS_CLIENT_AUTH) { + Some(s) => s + .parse() + .map_err(|_| zerror!("Unknown client auth argument: {}", s))?, + None => false, + }; + let tls_server_private_key = TlsServerConfig::load_tls_private_key(config).await?; + let tls_server_certificate = TlsServerConfig::load_tls_certificate(config).await?; + + let certs: Vec = + rustls_pemfile::certs(&mut Cursor::new(&tls_server_certificate)) + .collect::>() + .map_err(|err| zerror!("Error processing server certificate: {err}."))?; + + let mut keys: Vec = + rustls_pemfile::rsa_private_keys(&mut Cursor::new(&tls_server_private_key)) + .map(|x| x.map(PrivateKeyDer::from)) + .collect::>() + .map_err(|err| zerror!("Error processing server key: {err}."))?; + + if keys.is_empty() { + keys = rustls_pemfile::pkcs8_private_keys(&mut Cursor::new(&tls_server_private_key)) + .map(|x| x.map(PrivateKeyDer::from)) + .collect::>() + .map_err(|err| zerror!("Error processing server key: {err}."))?; + } + + if keys.is_empty() { + keys = rustls_pemfile::ec_private_keys(&mut Cursor::new(&tls_server_private_key)) + .map(|x| x.map(PrivateKeyDer::from)) + .collect::>() + .map_err(|err| zerror!("Error processing server key: {err}."))?; + } + + if keys.is_empty() { + bail!("No private key found for TLS server."); + } + + let sc = if tls_server_client_auth { + let root_cert_store = load_trust_anchors(config)?.map_or_else( + || { + Err(zerror!( + "Missing root certificates while client authentication is enabled." + )) + }, + Ok, + )?; + let client_auth = WebPkiClientVerifier::builder(root_cert_store.into()).build()?; + ServerConfig::builder_with_protocol_versions(&[&TLS13]) + .with_client_cert_verifier(client_auth) + .with_single_cert(certs, keys.remove(0)) + .map_err(|e| zerror!(e))? + } else { + ServerConfig::builder() + .with_no_client_auth() + .with_single_cert(certs, keys.remove(0)) + .map_err(|e| zerror!(e))? + }; + Ok(TlsServerConfig { server_config: sc }) + } + + async fn load_tls_private_key(config: &endpoint::Config<'_>) -> ZResult> { + load_tls_key( + config, + TLS_SERVER_PRIVATE_KEY_RAW, + TLS_SERVER_PRIVATE_KEY_FILE, + TLS_SERVER_PRIVATE_KEY_BASE_64, + ) + .await + } + + async fn load_tls_certificate(config: &endpoint::Config<'_>) -> ZResult> { + load_tls_certificate( + config, + TLS_SERVER_CERTIFICATE_RAW, + TLS_SERVER_CERTIFICATE_FILE, + TLS_SERVER_CERTIFICATE_BASE64, + ) + .await + } +} + +pub struct TlsClientConfig { + pub client_config: ClientConfig, +} + +impl TlsClientConfig { + pub async fn new(config: &endpoint::Config<'_>) -> ZResult { + let tls_client_server_auth: bool = match config.get(TLS_CLIENT_AUTH) { + Some(s) => s + .parse() + .map_err(|_| zerror!("Unknown client auth argument: {}", s))?, + None => false, + }; + + let tls_server_name_verification: bool = match config.get(TLS_SERVER_NAME_VERIFICATION) { + Some(s) => { + let s: bool = s + .parse() + .map_err(|_| zerror!("Unknown server name verification argument: {}", s))?; + if s { + log::warn!("Skipping name verification of servers"); + } + s + } + None => false, + }; + + // Allows mixed user-generated CA and webPKI CA + log::debug!("Loading default Web PKI certificates."); + let mut root_cert_store = RootCertStore { + roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(), + }; + + if let Some(custom_root_cert) = load_trust_anchors(config)? { + log::debug!("Loading user-generated certificates."); + root_cert_store.extend(custom_root_cert.roots); + } + + let cc = if tls_client_server_auth { + log::debug!("Loading client authentication key and certificate..."); + let tls_client_private_key = TlsClientConfig::load_tls_private_key(config).await?; + let tls_client_certificate = TlsClientConfig::load_tls_certificate(config).await?; + + let certs: Vec = + rustls_pemfile::certs(&mut Cursor::new(&tls_client_certificate)) + .collect::>() + .map_err(|err| zerror!("Error processing client certificate: {err}."))?; + + let mut keys: Vec = + rustls_pemfile::rsa_private_keys(&mut Cursor::new(&tls_client_private_key)) + .map(|x| x.map(PrivateKeyDer::from)) + .collect::>() + .map_err(|err| zerror!("Error processing client key: {err}."))?; + + if keys.is_empty() { + keys = + rustls_pemfile::pkcs8_private_keys(&mut Cursor::new(&tls_client_private_key)) + .map(|x| x.map(PrivateKeyDer::from)) + .collect::>() + .map_err(|err| zerror!("Error processing client key: {err}."))?; + } + + if keys.is_empty() { + keys = rustls_pemfile::ec_private_keys(&mut Cursor::new(&tls_client_private_key)) + .map(|x| x.map(PrivateKeyDer::from)) + .collect::>() + .map_err(|err| zerror!("Error processing client key: {err}."))?; + } + + if keys.is_empty() { + bail!("No private key found for TLS client."); + } + + let builder = ClientConfig::builder_with_protocol_versions(&[&TLS13]); + + if tls_server_name_verification { + builder + .with_root_certificates(root_cert_store) + .with_client_auth_cert(certs, keys.remove(0)) + } else { + builder + .dangerous() + .with_custom_certificate_verifier(Arc::new(WebPkiVerifierAnyServerName::new( + root_cert_store, + ))) + .with_client_auth_cert(certs, keys.remove(0)) + } + .map_err(|e| zerror!("Bad certificate/key: {}", e))? + } else { + let builder = ClientConfig::builder(); + if tls_server_name_verification { + builder + .with_root_certificates(root_cert_store) + .with_no_client_auth() + } else { + builder + .dangerous() + .with_custom_certificate_verifier(Arc::new(WebPkiVerifierAnyServerName::new( + root_cert_store, + ))) + .with_no_client_auth() + } + }; + Ok(TlsClientConfig { client_config: cc }) + } + + async fn load_tls_private_key(config: &endpoint::Config<'_>) -> ZResult> { + load_tls_key( + config, + TLS_CLIENT_PRIVATE_KEY_RAW, + TLS_CLIENT_PRIVATE_KEY_FILE, + TLS_CLIENT_PRIVATE_KEY_BASE64, + ) + .await + } + + async fn load_tls_certificate(config: &endpoint::Config<'_>) -> ZResult> { + load_tls_certificate( + config, + TLS_CLIENT_CERTIFICATE_RAW, + TLS_CLIENT_CERTIFICATE_FILE, + TLS_CLIENT_CERTIFICATE_BASE64, + ) + .await + } +} + +async fn load_tls_key( + config: &endpoint::Config<'_>, + tls_private_key_raw_config_key: &str, + tls_private_key_file_config_key: &str, + tls_private_key_base64_config_key: &str, +) -> ZResult> { + if let Some(value) = config.get(tls_private_key_raw_config_key) { + return Ok(value.as_bytes().to_vec()); + } + + if let Some(b64_key) = config.get(tls_private_key_base64_config_key) { + return base64_decode(b64_key); + } + + if let Some(value) = config.get(tls_private_key_file_config_key) { + return Ok(tokio::fs::read(value) + .await + .map_err(|e| zerror!("Invalid TLS private key file: {}", e))?) + .and_then(|result| { + if result.is_empty() { + Err(zerror!("Empty TLS key.").into()) + } else { + Ok(result) + } + }); + } + Err(zerror!("Missing TLS private key.").into()) +} + +async fn load_tls_certificate( + config: &endpoint::Config<'_>, + tls_certificate_raw_config_key: &str, + tls_certificate_file_config_key: &str, + tls_certificate_base64_config_key: &str, +) -> ZResult> { + if let Some(value) = config.get(tls_certificate_raw_config_key) { + return Ok(value.as_bytes().to_vec()); + } + + if let Some(b64_certificate) = config.get(tls_certificate_base64_config_key) { + return base64_decode(b64_certificate); + } + + if let Some(value) = config.get(tls_certificate_file_config_key) { + return Ok(tokio::fs::read(value) + .await + .map_err(|e| zerror!("Invalid TLS certificate file: {}", e))?); + } + Err(zerror!("Missing tls certificates.").into()) +} + +fn load_trust_anchors(config: &endpoint::Config<'_>) -> ZResult> { + let mut root_cert_store = RootCertStore::empty(); + if let Some(value) = config.get(TLS_ROOT_CA_CERTIFICATE_RAW) { + let mut pem = BufReader::new(value.as_bytes()); + let trust_anchors = process_pem(&mut pem)?; + root_cert_store.extend(trust_anchors); + return Ok(Some(root_cert_store)); + } + + if let Some(b64_certificate) = config.get(TLS_ROOT_CA_CERTIFICATE_BASE64) { + let certificate_pem = base64_decode(b64_certificate)?; + let mut pem = BufReader::new(certificate_pem.as_slice()); + let trust_anchors = process_pem(&mut pem)?; + root_cert_store.extend(trust_anchors); + return Ok(Some(root_cert_store)); + } + + if let Some(filename) = config.get(TLS_ROOT_CA_CERTIFICATE_FILE) { + let mut pem = BufReader::new(File::open(filename)?); + let trust_anchors = process_pem(&mut pem)?; + root_cert_store.extend(trust_anchors); + return Ok(Some(root_cert_store)); + } + Ok(None) +} + +fn process_pem(pem: &mut dyn BufRead) -> ZResult>> { + let certs: Vec = rustls_pemfile::certs(pem) + .map(|result| result.map_err(|err| zerror!("Error processing PEM certificates: {err}."))) + .collect::, ZError>>()?; + + let trust_anchors: Vec = certs + .into_iter() + .map(|cert| { + anchor_from_trusted_cert(&cert) + .map_err(|err| zerror!("Error processing trust anchor: {err}.")) + .map(|trust_anchor| trust_anchor.to_owned()) + }) + .collect::, ZError>>()?; + + Ok(trust_anchors) +} diff --git a/io/zenoh-links/zenoh-link-quic/Cargo.toml b/io/zenoh-links/zenoh-link-quic/Cargo.toml index a10e18fd43..84c4b035a2 100644 --- a/io/zenoh-links/zenoh-link-quic/Cargo.toml +++ b/io/zenoh-links/zenoh-link-quic/Cargo.toml @@ -30,15 +30,11 @@ base64 = { workspace = true } futures = { workspace = true } tracing = {workspace = true} quinn = { workspace = true } -rustls-native-certs = { workspace = true } -rustls-pemfile = { workspace = true } -rustls-webpki = { workspace = true } -secrecy = {workspace = true } tokio = { workspace = true, features = ["io-util", "net", "fs", "sync", "time"] } tokio-util = { workspace = true, features = ["rt"] } zenoh-config = { workspace = true } zenoh-core = { workspace = true } -zenoh-link-commons = { workspace = true } +zenoh-link-commons = { workspace = true, features = ["tls"]} zenoh-protocol = { workspace = true } zenoh-result = { workspace = true } zenoh-sync = { workspace = true } diff --git a/io/zenoh-links/zenoh-link-quic/src/unicast.rs b/io/zenoh-links/zenoh-link-quic/src/unicast.rs index 4c10315e61..a023c74b38 100644 --- a/io/zenoh-links/zenoh-link-quic/src/unicast.rs +++ b/io/zenoh-links/zenoh-link-quic/src/unicast.rs @@ -35,6 +35,7 @@ use zenoh_link_commons::tls::config::{ TLS_SERVER_NAME_VERIFICATION, TLS_SERVER_NAME_VERIFICATION_DEFAULT, TLS_SERVER_PRIVATE_KEY_FILE, TLS_SERVER_PRIVATE_KEY_RAW, }; +use zenoh_link_commons::tls::{TlsClientConfig, TlsServerConfig}; use zenoh_link_commons::{ get_ip_interface_names, LinkManagerUnicastTrait, LinkUnicast, LinkUnicastTrait, ListenersUnicastIP, NewLinkChannelSender, @@ -225,55 +226,12 @@ impl LinkManagerUnicastTrait for LinkManagerUnicastQuic { } // Initialize the QUIC connection - let mut root_cert_store = rustls::RootCertStore::empty(); - - // Read the certificates - let f = if let Some(value) = epconf.get(TLS_ROOT_CA_CERTIFICATE_RAW) { - value.as_bytes().to_vec() - } else if let Some(b64_certificate) = epconf.get(TLS_ROOT_CA_CERTIFICATE_BASE64) { - base64_decode(b64_certificate)? - } else if let Some(value) = epconf.get(TLS_ROOT_CA_CERTIFICATE_FILE) { - tokio::fs::read(value) - .await - .map_err(|e| zerror!("Invalid QUIC CA certificate file: {}", e))? - } else { - vec![] - }; - - let certificates = if f.is_empty() { - rustls_native_certs::load_native_certs() - .map_err(|e| zerror!("Invalid QUIC CA certificate file: {}", e))? - .drain(..) - .map(|x| rustls::Certificate(x.to_vec())) - .collect::>() - } else { - rustls_pemfile::certs(&mut BufReader::new(f.as_slice())) - .map(|result| { - result - .map_err(|err| zerror!("Invalid QUIC CA certificate file: {}", err)) - .map(|der| Certificate(der.to_vec())) - }) - .collect::, ZError>>()? - }; - for c in certificates.iter() { - root_cert_store.add(c).map_err(|e| zerror!("{}", e))?; - } - - let client_crypto = rustls::ClientConfig::builder().with_safe_defaults(); - - let mut client_crypto = if server_name_verification { - client_crypto - .with_root_certificates(root_cert_store) - .with_no_client_auth() - } else { - client_crypto - .with_custom_certificate_verifier(Arc::new(WebPkiVerifierAnyServerName::new( - root_cert_store, - ))) - .with_no_client_auth() - }; + let mut client_crypto = TlsClientConfig::new(&epconf) + .await + .map_err(|e| zerror!("Cannot create a new QUIC client on {addr}. {e}"))?; - client_crypto.alpn_protocols = ALPN_QUIC_HTTP.iter().map(|&x| x.into()).collect(); + client_crypto.client_config.alpn_protocols = + ALPN_QUIC_HTTP.iter().map(|&x| x.into()).collect(); let ip_addr: IpAddr = if addr.is_ipv4() { Ipv4Addr::UNSPECIFIED.into() @@ -282,7 +240,9 @@ impl LinkManagerUnicastTrait for LinkManagerUnicastQuic { }; let mut quic_endpoint = quinn::Endpoint::client(SocketAddr::new(ip_addr, 0)) .map_err(|e| zerror!("Can not create a new QUIC link bound to {}: {}", host, e))?; - quic_endpoint.set_default_client_config(quinn::ClientConfig::new(Arc::new(client_crypto))); + quic_endpoint.set_default_client_config(quinn::ClientConfig::new(Arc::new( + client_crypto.client_config, + ))); let src_addr = quic_endpoint .local_addr() @@ -320,61 +280,14 @@ impl LinkManagerUnicastTrait for LinkManagerUnicastQuic { let addr = get_quic_addr(&epaddr).await?; - let f = if let Some(value) = epconf.get(TLS_SERVER_CERTIFICATE_RAW) { - value.as_bytes().to_vec() - } else if let Some(b64_certificate) = epconf.get(TLS_SERVER_CERTIFICATE_BASE64) { - base64_decode(b64_certificate)? - } else if let Some(value) = epconf.get(TLS_SERVER_CERTIFICATE_FILE) { - tokio::fs::read(value) - .await - .map_err(|e| zerror!("Invalid QUIC CA certificate file: {}", e))? - } else { - bail!("No QUIC CA certificate has been provided."); - }; - let certificates = rustls_pemfile::certs(&mut BufReader::new(f.as_slice())) - .map(|result| { - result - .map_err(|err| zerror!("Invalid QUIC CA certificate file: {}", err)) - .map(|der| Certificate(der.to_vec())) - }) - .collect::, ZError>>()?; - - // Private keys - let f = if let Some(value) = epconf.get(TLS_SERVER_PRIVATE_KEY_RAW) { - value.as_bytes().to_vec() - } else if let Some(b64_key) = epconf.get(TLS_SERVER_PRIVATE_KEY_RAW) { - base64_decode(b64_key)? - } else if let Some(value) = epconf.get(TLS_SERVER_PRIVATE_KEY_FILE) { - tokio::fs::read(value) - .await - .map_err(|e| zerror!("Invalid QUIC CA certificate file: {}", e))? - } else { - bail!("No QUIC CA private key has been provided."); - }; - let items: Vec = rustls_pemfile::read_all(&mut BufReader::new(f.as_slice())) - .collect::>() - .map_err(|err| zerror!("Invalid QUIC CA private key file: {}", err))?; - - let private_key = items - .into_iter() - .filter_map(|x| match x { - rustls_pemfile::Item::Pkcs1Key(k) => Some(k.secret_pkcs1_der().to_vec()), - rustls_pemfile::Item::Pkcs8Key(k) => Some(k.secret_pkcs8_der().to_vec()), - rustls_pemfile::Item::Sec1Key(k) => Some(k.secret_sec1_der().to_vec()), - _ => None, - }) - .take(1) - .next() - .ok_or_else(|| zerror!("No QUIC CA private key has been provided.")) - .map(PrivateKey)?; - // Server config - let mut server_crypto = rustls::ServerConfig::builder() - .with_safe_defaults() - .with_no_client_auth() - .with_single_cert(certificates, private_key)?; - server_crypto.alpn_protocols = ALPN_QUIC_HTTP.iter().map(|&x| x.into()).collect(); - let mut server_config = quinn::ServerConfig::with_crypto(Arc::new(server_crypto)); + let mut server_crypto = TlsServerConfig::new(&epconf) + .await + .map_err(|e| zerror!("Cannot create a new QUIC listener on {addr}. {e}"))?; + server_crypto.server_config.alpn_protocols = + ALPN_QUIC_HTTP.iter().map(|&x| x.into()).collect(); + let mut server_config = + quinn::ServerConfig::with_crypto(Arc::new(server_crypto.server_config)); // We do not accept unidireactional streams. Arc::get_mut(&mut server_config.transport) diff --git a/io/zenoh-links/zenoh-link-tls/Cargo.toml b/io/zenoh-links/zenoh-link-tls/Cargo.toml index 11d00d96d8..dbf63ed157 100644 --- a/io/zenoh-links/zenoh-link-tls/Cargo.toml +++ b/io/zenoh-links/zenoh-link-tls/Cargo.toml @@ -37,10 +37,9 @@ secrecy = {workspace = true } tokio = { workspace = true, features = ["io-util", "net", "fs", "sync"] } tokio-rustls = { workspace = true } tokio-util = { workspace = true, features = ["rt"] } -webpki-roots = { workspace = true } zenoh-config = { workspace = true } zenoh-core = { workspace = true } -zenoh-link-commons = { workspace = true } +zenoh-link-commons = { workspace = true, features = ["tls"]} zenoh-protocol = { workspace = true } zenoh-result = { workspace = true } zenoh-runtime = { workspace = true } diff --git a/io/zenoh-links/zenoh-link-tls/src/unicast.rs b/io/zenoh-links/zenoh-link-tls/src/unicast.rs index 5e7eb78e22..742929941c 100644 --- a/io/zenoh-links/zenoh-link-tls/src/unicast.rs +++ b/io/zenoh-links/zenoh-link-tls/src/unicast.rs @@ -46,7 +46,7 @@ use zenoh_link_commons::tls::{ TLS_SERVER_CERTIFICATE_RAW, TLS_SERVER_NAME_VERIFICATION, TLS_SERVER_PRIVATE_KEY_BASE_64, TLS_SERVER_PRIVATE_KEY_FILE, TLS_SERVER_PRIVATE_KEY_RAW, }, - WebPkiVerifierAnyServerName, + TlsClientConfig, TlsServerConfig, WebPkiVerifierAnyServerName, }; use zenoh_link_commons::{ get_ip_interface_names, LinkManagerUnicastTrait, LinkUnicast, LinkUnicastTrait, @@ -428,311 +428,3 @@ async fn accept_task( Ok(()) } - -struct TlsServerConfig { - server_config: ServerConfig, -} - -impl TlsServerConfig { - pub async fn new(config: &Config<'_>) -> ZResult { - let tls_server_client_auth: bool = match config.get(TLS_CLIENT_AUTH) { - Some(s) => s - .parse() - .map_err(|_| zerror!("Unknown client auth argument: {}", s))?, - None => false, - }; - let tls_server_private_key = TlsServerConfig::load_tls_private_key(config).await?; - let tls_server_certificate = TlsServerConfig::load_tls_certificate(config).await?; - - let certs: Vec = - rustls_pemfile::certs(&mut Cursor::new(&tls_server_certificate)) - .collect::>() - .map_err(|err| zerror!("Error processing server certificate: {err}."))?; - - let mut keys: Vec = - rustls_pemfile::rsa_private_keys(&mut Cursor::new(&tls_server_private_key)) - .map(|x| x.map(PrivateKeyDer::from)) - .collect::>() - .map_err(|err| zerror!("Error processing server key: {err}."))?; - - if keys.is_empty() { - keys = rustls_pemfile::pkcs8_private_keys(&mut Cursor::new(&tls_server_private_key)) - .map(|x| x.map(PrivateKeyDer::from)) - .collect::>() - .map_err(|err| zerror!("Error processing server key: {err}."))?; - } - - if keys.is_empty() { - keys = rustls_pemfile::ec_private_keys(&mut Cursor::new(&tls_server_private_key)) - .map(|x| x.map(PrivateKeyDer::from)) - .collect::>() - .map_err(|err| zerror!("Error processing server key: {err}."))?; - } - - if keys.is_empty() { - bail!("No private key found for TLS server."); - } - - let sc = if tls_server_client_auth { - let root_cert_store = load_trust_anchors(config)?.map_or_else( - || { - Err(zerror!( - "Missing root certificates while client authentication is enabled." - )) - }, - Ok, - )?; - let client_auth = WebPkiClientVerifier::builder(root_cert_store.into()).build()?; - ServerConfig::builder_with_protocol_versions(&[&TLS13]) - .with_client_cert_verifier(client_auth) - .with_single_cert(certs, keys.remove(0)) - .map_err(|e| zerror!(e))? - } else { - ServerConfig::builder() - .with_no_client_auth() - .with_single_cert(certs, keys.remove(0)) - .map_err(|e| zerror!(e))? - }; - Ok(TlsServerConfig { server_config: sc }) - } - - async fn load_tls_private_key(config: &Config<'_>) -> ZResult> { - load_tls_key( - config, - TLS_SERVER_PRIVATE_KEY_RAW, - TLS_SERVER_PRIVATE_KEY_FILE, - TLS_SERVER_PRIVATE_KEY_BASE_64, - ) - .await - } - - async fn load_tls_certificate(config: &Config<'_>) -> ZResult> { - load_tls_certificate( - config, - TLS_SERVER_CERTIFICATE_RAW, - TLS_SERVER_CERTIFICATE_FILE, - TLS_SERVER_CERTIFICATE_BASE64, - ) - .await - } -} - -struct TlsClientConfig { - client_config: ClientConfig, -} - -impl TlsClientConfig { - pub async fn new(config: &Config<'_>) -> ZResult { - let tls_client_server_auth: bool = match config.get(TLS_CLIENT_AUTH) { - Some(s) => s - .parse() - .map_err(|_| zerror!("Unknown client auth argument: {}", s))?, - None => false, - }; - - let tls_server_name_verification: bool = match config.get(TLS_SERVER_NAME_VERIFICATION) { - Some(s) => { - let s: bool = s - .parse() - .map_err(|_| zerror!("Unknown server name verification argument: {}", s))?; - if s { - tracing::warn!("Skipping name verification of servers"); - } - s - } - None => false, - }; - - // Allows mixed user-generated CA and webPKI CA - tracing::debug!("Loading default Web PKI certificates."); - let mut root_cert_store = RootCertStore { - roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(), - }; - - if let Some(custom_root_cert) = load_trust_anchors(config)? { - tracing::debug!("Loading user-generated certificates."); - root_cert_store.extend(custom_root_cert.roots); - } - - let cc = if tls_client_server_auth { - tracing::debug!("Loading client authentication key and certificate..."); - let tls_client_private_key = TlsClientConfig::load_tls_private_key(config).await?; - let tls_client_certificate = TlsClientConfig::load_tls_certificate(config).await?; - - let certs: Vec = - rustls_pemfile::certs(&mut Cursor::new(&tls_client_certificate)) - .collect::>() - .map_err(|err| zerror!("Error processing client certificate: {err}."))?; - - let mut keys: Vec = - rustls_pemfile::rsa_private_keys(&mut Cursor::new(&tls_client_private_key)) - .map(|x| x.map(PrivateKeyDer::from)) - .collect::>() - .map_err(|err| zerror!("Error processing client key: {err}."))?; - - if keys.is_empty() { - keys = - rustls_pemfile::pkcs8_private_keys(&mut Cursor::new(&tls_client_private_key)) - .map(|x| x.map(PrivateKeyDer::from)) - .collect::>() - .map_err(|err| zerror!("Error processing client key: {err}."))?; - } - - if keys.is_empty() { - keys = rustls_pemfile::ec_private_keys(&mut Cursor::new(&tls_client_private_key)) - .map(|x| x.map(PrivateKeyDer::from)) - .collect::>() - .map_err(|err| zerror!("Error processing client key: {err}."))?; - } - - if keys.is_empty() { - bail!("No private key found for TLS client."); - } - - let builder = ClientConfig::builder_with_protocol_versions(&[&TLS13]); - - if tls_server_name_verification { - builder - .with_root_certificates(root_cert_store) - .with_client_auth_cert(certs, keys.remove(0)) - } else { - builder - .dangerous() - .with_custom_certificate_verifier(Arc::new(WebPkiVerifierAnyServerName::new( - root_cert_store, - ))) - .with_client_auth_cert(certs, keys.remove(0)) - } - .map_err(|e| zerror!("Bad certificate/key: {}", e))? - } else { - let builder = ClientConfig::builder(); - if tls_server_name_verification { - builder - .with_root_certificates(root_cert_store) - .with_no_client_auth() - } else { - builder - .dangerous() - .with_custom_certificate_verifier(Arc::new(WebPkiVerifierAnyServerName::new( - root_cert_store, - ))) - .with_no_client_auth() - } - }; - Ok(TlsClientConfig { client_config: cc }) - } - - async fn load_tls_private_key(config: &Config<'_>) -> ZResult> { - load_tls_key( - config, - TLS_CLIENT_PRIVATE_KEY_RAW, - TLS_CLIENT_PRIVATE_KEY_FILE, - TLS_CLIENT_PRIVATE_KEY_BASE64, - ) - .await - } - - async fn load_tls_certificate(config: &Config<'_>) -> ZResult> { - load_tls_certificate( - config, - TLS_CLIENT_CERTIFICATE_RAW, - TLS_CLIENT_CERTIFICATE_FILE, - TLS_CLIENT_CERTIFICATE_BASE64, - ) - .await - } -} - -async fn load_tls_key( - config: &Config<'_>, - tls_private_key_raw_config_key: &str, - tls_private_key_file_config_key: &str, - tls_private_key_base64_config_key: &str, -) -> ZResult> { - if let Some(value) = config.get(tls_private_key_raw_config_key) { - return Ok(value.as_bytes().to_vec()); - } - - if let Some(b64_key) = config.get(tls_private_key_base64_config_key) { - return base64_decode(b64_key); - } - - if let Some(value) = config.get(tls_private_key_file_config_key) { - return Ok(tokio::fs::read(value) - .await - .map_err(|e| zerror!("Invalid TLS private key file: {}", e))?) - .and_then(|result| { - if result.is_empty() { - Err(zerror!("Empty TLS key.").into()) - } else { - Ok(result) - } - }); - } - Err(zerror!("Missing TLS private key.").into()) -} - -async fn load_tls_certificate( - config: &Config<'_>, - tls_certificate_raw_config_key: &str, - tls_certificate_file_config_key: &str, - tls_certificate_base64_config_key: &str, -) -> ZResult> { - if let Some(value) = config.get(tls_certificate_raw_config_key) { - return Ok(value.as_bytes().to_vec()); - } - - if let Some(b64_certificate) = config.get(tls_certificate_base64_config_key) { - return base64_decode(b64_certificate); - } - - if let Some(value) = config.get(tls_certificate_file_config_key) { - return Ok(tokio::fs::read(value) - .await - .map_err(|e| zerror!("Invalid TLS certificate file: {}", e))?); - } - Err(zerror!("Missing tls certificates.").into()) -} - -fn load_trust_anchors(config: &Config<'_>) -> ZResult> { - let mut root_cert_store = RootCertStore::empty(); - if let Some(value) = config.get(TLS_ROOT_CA_CERTIFICATE_RAW) { - let mut pem = BufReader::new(value.as_bytes()); - let trust_anchors = process_pem(&mut pem)?; - root_cert_store.extend(trust_anchors); - return Ok(Some(root_cert_store)); - } - - if let Some(b64_certificate) = config.get(TLS_ROOT_CA_CERTIFICATE_BASE64) { - let certificate_pem = base64_decode(b64_certificate)?; - let mut pem = BufReader::new(certificate_pem.as_slice()); - let trust_anchors = process_pem(&mut pem)?; - root_cert_store.extend(trust_anchors); - return Ok(Some(root_cert_store)); - } - - if let Some(filename) = config.get(TLS_ROOT_CA_CERTIFICATE_FILE) { - let mut pem = BufReader::new(File::open(filename)?); - let trust_anchors = process_pem(&mut pem)?; - root_cert_store.extend(trust_anchors); - return Ok(Some(root_cert_store)); - } - Ok(None) -} - -fn process_pem(pem: &mut dyn io::BufRead) -> ZResult>> { - let certs: Vec = rustls_pemfile::certs(pem) - .map(|result| result.map_err(|err| zerror!("Error processing PEM certificates: {err}."))) - .collect::, ZError>>()?; - - let trust_anchors: Vec = certs - .into_iter() - .map(|cert| { - anchor_from_trusted_cert(&cert) - .map_err(|err| zerror!("Error processing trust anchor: {err}.")) - .map(|trust_anchor| trust_anchor.to_owned()) - }) - .collect::, ZError>>()?; - - Ok(trust_anchors) -} From b5746c5ece4e1299351db316769955d529840b16 Mon Sep 17 00:00:00 2001 From: gabrik Date: Wed, 24 Apr 2024 11:03:24 +0200 Subject: [PATCH 3/6] fix(mtls-quic): using current release of quinn at the cost of some duplicated code Signed-off-by: gabrik --- Cargo.lock | 177 +++++- io/zenoh-link-commons/Cargo.toml | 13 +- io/zenoh-link-commons/src/lib.rs | 1 - io/zenoh-link-commons/src/tls.rs | 482 +---------------- io/zenoh-link/src/lib.rs | 13 +- io/zenoh-links/zenoh-link-quic/Cargo.toml | 35 +- io/zenoh-links/zenoh-link-quic/src/lib.rs | 43 +- io/zenoh-links/zenoh-link-quic/src/unicast.rs | 19 +- io/zenoh-links/zenoh-link-quic/src/utils.rs | 509 ++++++++++++++++++ io/zenoh-links/zenoh-link-tls/Cargo.toml | 23 +- io/zenoh-links/zenoh-link-tls/src/lib.rs | 51 +- io/zenoh-links/zenoh-link-tls/src/unicast.rs | 30 +- io/zenoh-links/zenoh-link-tls/src/utils.rs | 480 +++++++++++++++++ io/zenoh-transport/tests/endpoints.rs | 4 +- .../tests/unicast_authenticator.rs | 4 +- io/zenoh-transport/tests/unicast_multilink.rs | 4 +- io/zenoh-transport/tests/unicast_openclose.rs | 4 +- io/zenoh-transport/tests/unicast_transport.rs | 10 +- 18 files changed, 1280 insertions(+), 622 deletions(-) create mode 100644 io/zenoh-links/zenoh-link-quic/src/utils.rs create mode 100644 io/zenoh-links/zenoh-link-tls/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index ed925977e2..e2b5be916b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -451,6 +451,33 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "aws-lc-rs" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5509d663b2c00ee421bda8d6a24d6c42e15970957de1701b8df9f6fbe5707df1" +dependencies = [ + "aws-lc-sys", + "mirai-annotations", + "paste", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5d317212c2a78d86ba6622e969413c38847b62f48111f8b763af3dac2f9840" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", + "libc", + "paste", +] + [[package]] name = "backtrace" version = "0.3.69" @@ -505,6 +532,29 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags 2.4.2", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.33", + "which", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -625,9 +675,19 @@ version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ + "jobserver", "libc", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "0.1.10" @@ -701,6 +761,17 @@ dependencies = [ "inout", ] +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.4.11" @@ -741,6 +812,15 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + [[package]] name = "cobs" version = "0.2.3" @@ -1082,6 +1162,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + [[package]] name = "dyn-clone" version = "1.0.13" @@ -1275,6 +1361,12 @@ dependencies = [ "num", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures" version = "0.3.28" @@ -1451,6 +1543,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "gloo-timers" version = "0.2.6" @@ -1817,6 +1915,15 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "jobserver" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.64" @@ -1901,6 +2008,12 @@ dependencies = [ "spin 0.5.2", ] +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.153" @@ -2080,6 +2193,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "mirai-annotations" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" + [[package]] name = "nanorand" version = "0.7.0" @@ -2646,6 +2765,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "prettyplease" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +dependencies = [ + "proc-macro2", + "syn 2.0.33", +] + [[package]] name = "proc-macro-hack" version = "0.5.20+deprecated" @@ -3118,11 +3247,13 @@ dependencies = [ [[package]] name = "rustls" -version = "0.22.2" +version = "0.23.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" +checksum = "afabcee0551bd1aa3e18e5adbf2c0544722014b899adb31bd186ec638d3da97e" dependencies = [ + "aws-lc-rs", "log", + "once_cell", "ring 0.17.6", "rustls-pki-types", "rustls-webpki 0.102.2", @@ -3196,6 +3327,7 @@ version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ + "aws-lc-rs", "ring 0.17.6", "rustls-pki-types", "untrusted 0.9.0", @@ -3514,6 +3646,12 @@ dependencies = [ "dirs", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook" version = "0.3.17" @@ -4037,11 +4175,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.25.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.22.2", + "rustls 0.23.5", "rustls-pki-types", "tokio", ] @@ -4619,6 +4757,18 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.32", +] + [[package]] name = "win-sys" version = "0.3.1" @@ -5109,15 +5259,16 @@ name = "zenoh-link-commons" version = "0.11.0-dev" dependencies = [ "async-trait", + "base64 0.21.4", "flume", "futures", - "rustls 0.22.2", + "rustls 0.23.5", "rustls-webpki 0.102.2", - "secrecy", "serde", "tokio", "tokio-util", "tracing", + "webpki-roots", "zenoh-buffers", "zenoh-codec", "zenoh-config", @@ -5138,13 +5289,15 @@ dependencies = [ "quinn", "rustls 0.21.7", "rustls-native-certs 0.7.0", - "rustls-pemfile 2.0.0", + "rustls-pemfile 1.0.3", + "rustls-pki-types", "rustls-webpki 0.102.2", "secrecy", "tokio", "tokio-rustls 0.24.1", "tokio-util", "tracing", + "webpki-roots", "zenoh-config", "zenoh-core", "zenoh-link-commons", @@ -5200,13 +5353,13 @@ dependencies = [ "async-trait", "base64 0.21.4", "futures", - "rustls 0.22.2", + "rustls 0.23.5", "rustls-pemfile 2.0.0", "rustls-pki-types", "rustls-webpki 0.102.2", "secrecy", "tokio", - "tokio-rustls 0.25.0", + "tokio-rustls 0.26.0", "tokio-util", "tracing", "webpki-roots", @@ -5608,6 +5761,6 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/io/zenoh-link-commons/Cargo.toml b/io/zenoh-link-commons/Cargo.toml index 9b169ce8d0..12b70cad6d 100644 --- a/io/zenoh-link-commons/Cargo.toml +++ b/io/zenoh-link-commons/Cargo.toml @@ -26,20 +26,14 @@ version = { workspace = true } [features] compression = [] -tls = [ - "base64", - "rustls", - "rustls-native-certs", - "rustls-pemfile", - "rustls-webpki", - "webpki-roots", -] [dependencies] async-trait = { workspace = true } base64 = { workspace = true, optional = true } flume = { workspace = true } -tracing = {workspace = true} +futures = { workspace = true } +rustls = { workspace = true } +rustls-webpki = { workspace = true } serde = { workspace = true, features = ["default"] } tokio = { workspace = true, features = [ "fs", @@ -49,6 +43,7 @@ tokio = { workspace = true, features = [ "time", ] } tokio-util = { workspace = true, features = ["rt"] } +tracing = { workspace = true } webpki-roots = { workspace = true, optional = true } zenoh-buffers = { workspace = true } zenoh-codec = { workspace = true } diff --git a/io/zenoh-link-commons/src/lib.rs b/io/zenoh-link-commons/src/lib.rs index 7a115530a9..1bb081ded1 100644 --- a/io/zenoh-link-commons/src/lib.rs +++ b/io/zenoh-link-commons/src/lib.rs @@ -21,7 +21,6 @@ extern crate alloc; mod listener; mod multicast; -#[cfg(feature = "tls")] pub mod tls; mod unicast; diff --git a/io/zenoh-link-commons/src/tls.rs b/io/zenoh-link-commons/src/tls.rs index 2f6051580f..562b02c81e 100644 --- a/io/zenoh-link-commons/src/tls.rs +++ b/io/zenoh-link-commons/src/tls.rs @@ -1,4 +1,3 @@ -use crate::ConfigurationInspector; use alloc::vec::Vec; use rustls::{ client::{ @@ -6,39 +5,11 @@ use rustls::{ verify_server_cert_signed_by_trust_anchor, }, crypto::{verify_tls12_signature, verify_tls13_signature}, - pki_types::{CertificateDer, PrivateKeyDer, ServerName, TrustAnchor, UnixTime}, - server::{ParsedCertificate, WebPkiClientVerifier}, - version::TLS13, - ClientConfig, RootCertStore, ServerConfig, -}; -use secrecy::ExposeSecret; -use std::{ - fs::File, - io::{BufRead, BufReader, Cursor}, - sync::Arc, -}; -use webpki::{anchor_from_trusted_cert, ALL_VERIFICATION_ALGS}; -use zenoh_config::Config; -use zenoh_core::zerror; -use zenoh_protocol::core::endpoint; -use zenoh_result::{bail, ZError, ZResult}; - -use config::{ - TLS_CLIENT_AUTH, TLS_CLIENT_CERTIFICATE_BASE64, TLS_CLIENT_CERTIFICATE_FILE, - TLS_CLIENT_CERTIFICATE_RAW, TLS_CLIENT_PRIVATE_KEY_BASE64, TLS_CLIENT_PRIVATE_KEY_FILE, - TLS_CLIENT_PRIVATE_KEY_RAW, TLS_ROOT_CA_CERTIFICATE_BASE64, TLS_ROOT_CA_CERTIFICATE_FILE, - TLS_ROOT_CA_CERTIFICATE_RAW, TLS_SERVER_CERTIFICATE_BASE64, TLS_SERVER_CERTIFICATE_FILE, - TLS_SERVER_CERTIFICATE_RAW, TLS_SERVER_NAME_VERIFICATION, TLS_SERVER_PRIVATE_KEY_BASE_64, - TLS_SERVER_PRIVATE_KEY_FILE, TLS_SERVER_PRIVATE_KEY_RAW, + pki_types::{CertificateDer, ServerName, UnixTime}, + server::ParsedCertificate, + RootCertStore, }; - -pub fn base64_decode(data: &str) -> ZResult> { - use base64::engine::general_purpose; - use base64::Engine; - Ok(general_purpose::STANDARD - .decode(data) - .map_err(|e| zerror!("Unable to perform base64 decoding: {e:?}"))?) -} +use webpki::ALL_VERIFICATION_ALGS; impl ServerCertVerifier for WebPkiVerifierAnyServerName { /// Will verify the certificate is valid in the following ways: @@ -114,448 +85,3 @@ impl WebPkiVerifierAnyServerName { Self { roots } } } - -pub mod config { - pub const TLS_ROOT_CA_CERTIFICATE_FILE: &str = "root_ca_certificate_file"; - pub const TLS_ROOT_CA_CERTIFICATE_RAW: &str = "root_ca_certificate_raw"; - pub const TLS_ROOT_CA_CERTIFICATE_BASE64: &str = "root_ca_certificate_base64"; - - pub const TLS_SERVER_PRIVATE_KEY_FILE: &str = "server_private_key_file"; - pub const TLS_SERVER_PRIVATE_KEY_RAW: &str = "server_private_key_raw"; - pub const TLS_SERVER_PRIVATE_KEY_BASE_64: &str = "server_private_key_base64"; - - pub const TLS_SERVER_CERTIFICATE_FILE: &str = "server_certificate_file"; - pub const TLS_SERVER_CERTIFICATE_RAW: &str = "server_certificate_raw"; - pub const TLS_SERVER_CERTIFICATE_BASE64: &str = "server_certificate_base64"; - - pub const TLS_CLIENT_PRIVATE_KEY_FILE: &str = "client_private_key_file"; - pub const TLS_CLIENT_PRIVATE_KEY_RAW: &str = "client_private_key_raw"; - pub const TLS_CLIENT_PRIVATE_KEY_BASE64: &str = "client_private_key_base64"; - - pub const TLS_CLIENT_CERTIFICATE_FILE: &str = "client_certificate_file"; - pub const TLS_CLIENT_CERTIFICATE_RAW: &str = "client_certificate_raw"; - pub const TLS_CLIENT_CERTIFICATE_BASE64: &str = "client_certificate_base64"; - - pub const TLS_CLIENT_AUTH: &str = "client_auth"; - - pub const TLS_SERVER_NAME_VERIFICATION: &str = "server_name_verification"; - pub const TLS_SERVER_NAME_VERIFICATION_DEFAULT: &str = "true"; -} - -#[derive(Default, Clone, Copy, Debug)] -pub struct TlsConfigurator; - -impl ConfigurationInspector for TlsConfigurator { - fn inspect_config(&self, config: &Config) -> ZResult { - let mut ps: Vec<(&str, &str)> = vec![]; - - let c = config.transport().link().tls(); - - match (c.root_ca_certificate(), c.root_ca_certificate_base64()) { - (Some(_), Some(_)) => { - bail!("Only one between 'root_ca_certificate' and 'root_ca_certificate_base64' can be present!") - } - (Some(ca_certificate), None) => { - ps.push((TLS_ROOT_CA_CERTIFICATE_FILE, ca_certificate)); - } - (None, Some(ca_certificate)) => { - ps.push(( - TLS_ROOT_CA_CERTIFICATE_BASE64, - ca_certificate.expose_secret(), - )); - } - _ => {} - } - - match (c.server_private_key(), c.server_private_key_base64()) { - (Some(_), Some(_)) => { - bail!("Only one between 'server_private_key' and 'server_private_key_base64' can be present!") - } - (Some(server_private_key), None) => { - ps.push((TLS_SERVER_PRIVATE_KEY_FILE, server_private_key)); - } - (None, Some(server_private_key)) => { - ps.push(( - TLS_SERVER_PRIVATE_KEY_BASE_64, - server_private_key.expose_secret(), - )); - } - _ => {} - } - - match (c.server_certificate(), c.server_certificate_base64()) { - (Some(_), Some(_)) => { - bail!("Only one between 'server_certificate' and 'server_certificate_base64' can be present!") - } - (Some(server_certificate), None) => { - ps.push((TLS_SERVER_CERTIFICATE_FILE, server_certificate)); - } - (None, Some(server_certificate)) => { - ps.push(( - TLS_SERVER_CERTIFICATE_BASE64, - server_certificate.expose_secret(), - )); - } - _ => {} - } - - if let Some(client_auth) = c.client_auth() { - match client_auth { - true => ps.push((TLS_CLIENT_AUTH, "true")), - false => ps.push((TLS_CLIENT_AUTH, "false")), - }; - } - - match (c.client_private_key(), c.client_private_key_base64()) { - (Some(_), Some(_)) => { - bail!("Only one between 'client_private_key' and 'client_private_key_base64' can be present!") - } - (Some(client_private_key), None) => { - ps.push((TLS_CLIENT_PRIVATE_KEY_FILE, client_private_key)); - } - (None, Some(client_private_key)) => { - ps.push(( - TLS_CLIENT_PRIVATE_KEY_BASE64, - client_private_key.expose_secret(), - )); - } - _ => {} - } - - match (c.client_certificate(), c.client_certificate_base64()) { - (Some(_), Some(_)) => { - bail!("Only one between 'client_certificate' and 'client_certificate_base64' can be present!") - } - (Some(client_certificate), None) => { - ps.push((TLS_CLIENT_CERTIFICATE_FILE, client_certificate)); - } - (None, Some(client_certificate)) => { - ps.push(( - TLS_CLIENT_CERTIFICATE_BASE64, - client_certificate.expose_secret(), - )); - } - _ => {} - } - - if let Some(server_name_verification) = c.server_name_verification() { - match server_name_verification { - true => ps.push((TLS_SERVER_NAME_VERIFICATION, "true")), - false => ps.push((TLS_SERVER_NAME_VERIFICATION, "false")), - }; - } - - let mut s = String::new(); - endpoint::Parameters::extend(ps.drain(..), &mut s); - - Ok(s) - } -} - -pub struct TlsServerConfig { - pub server_config: ServerConfig, -} - -impl TlsServerConfig { - pub async fn new(config: &endpoint::Config<'_>) -> ZResult { - let tls_server_client_auth: bool = match config.get(TLS_CLIENT_AUTH) { - Some(s) => s - .parse() - .map_err(|_| zerror!("Unknown client auth argument: {}", s))?, - None => false, - }; - let tls_server_private_key = TlsServerConfig::load_tls_private_key(config).await?; - let tls_server_certificate = TlsServerConfig::load_tls_certificate(config).await?; - - let certs: Vec = - rustls_pemfile::certs(&mut Cursor::new(&tls_server_certificate)) - .collect::>() - .map_err(|err| zerror!("Error processing server certificate: {err}."))?; - - let mut keys: Vec = - rustls_pemfile::rsa_private_keys(&mut Cursor::new(&tls_server_private_key)) - .map(|x| x.map(PrivateKeyDer::from)) - .collect::>() - .map_err(|err| zerror!("Error processing server key: {err}."))?; - - if keys.is_empty() { - keys = rustls_pemfile::pkcs8_private_keys(&mut Cursor::new(&tls_server_private_key)) - .map(|x| x.map(PrivateKeyDer::from)) - .collect::>() - .map_err(|err| zerror!("Error processing server key: {err}."))?; - } - - if keys.is_empty() { - keys = rustls_pemfile::ec_private_keys(&mut Cursor::new(&tls_server_private_key)) - .map(|x| x.map(PrivateKeyDer::from)) - .collect::>() - .map_err(|err| zerror!("Error processing server key: {err}."))?; - } - - if keys.is_empty() { - bail!("No private key found for TLS server."); - } - - let sc = if tls_server_client_auth { - let root_cert_store = load_trust_anchors(config)?.map_or_else( - || { - Err(zerror!( - "Missing root certificates while client authentication is enabled." - )) - }, - Ok, - )?; - let client_auth = WebPkiClientVerifier::builder(root_cert_store.into()).build()?; - ServerConfig::builder_with_protocol_versions(&[&TLS13]) - .with_client_cert_verifier(client_auth) - .with_single_cert(certs, keys.remove(0)) - .map_err(|e| zerror!(e))? - } else { - ServerConfig::builder() - .with_no_client_auth() - .with_single_cert(certs, keys.remove(0)) - .map_err(|e| zerror!(e))? - }; - Ok(TlsServerConfig { server_config: sc }) - } - - async fn load_tls_private_key(config: &endpoint::Config<'_>) -> ZResult> { - load_tls_key( - config, - TLS_SERVER_PRIVATE_KEY_RAW, - TLS_SERVER_PRIVATE_KEY_FILE, - TLS_SERVER_PRIVATE_KEY_BASE_64, - ) - .await - } - - async fn load_tls_certificate(config: &endpoint::Config<'_>) -> ZResult> { - load_tls_certificate( - config, - TLS_SERVER_CERTIFICATE_RAW, - TLS_SERVER_CERTIFICATE_FILE, - TLS_SERVER_CERTIFICATE_BASE64, - ) - .await - } -} - -pub struct TlsClientConfig { - pub client_config: ClientConfig, -} - -impl TlsClientConfig { - pub async fn new(config: &endpoint::Config<'_>) -> ZResult { - let tls_client_server_auth: bool = match config.get(TLS_CLIENT_AUTH) { - Some(s) => s - .parse() - .map_err(|_| zerror!("Unknown client auth argument: {}", s))?, - None => false, - }; - - let tls_server_name_verification: bool = match config.get(TLS_SERVER_NAME_VERIFICATION) { - Some(s) => { - let s: bool = s - .parse() - .map_err(|_| zerror!("Unknown server name verification argument: {}", s))?; - if s { - log::warn!("Skipping name verification of servers"); - } - s - } - None => false, - }; - - // Allows mixed user-generated CA and webPKI CA - log::debug!("Loading default Web PKI certificates."); - let mut root_cert_store = RootCertStore { - roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(), - }; - - if let Some(custom_root_cert) = load_trust_anchors(config)? { - log::debug!("Loading user-generated certificates."); - root_cert_store.extend(custom_root_cert.roots); - } - - let cc = if tls_client_server_auth { - log::debug!("Loading client authentication key and certificate..."); - let tls_client_private_key = TlsClientConfig::load_tls_private_key(config).await?; - let tls_client_certificate = TlsClientConfig::load_tls_certificate(config).await?; - - let certs: Vec = - rustls_pemfile::certs(&mut Cursor::new(&tls_client_certificate)) - .collect::>() - .map_err(|err| zerror!("Error processing client certificate: {err}."))?; - - let mut keys: Vec = - rustls_pemfile::rsa_private_keys(&mut Cursor::new(&tls_client_private_key)) - .map(|x| x.map(PrivateKeyDer::from)) - .collect::>() - .map_err(|err| zerror!("Error processing client key: {err}."))?; - - if keys.is_empty() { - keys = - rustls_pemfile::pkcs8_private_keys(&mut Cursor::new(&tls_client_private_key)) - .map(|x| x.map(PrivateKeyDer::from)) - .collect::>() - .map_err(|err| zerror!("Error processing client key: {err}."))?; - } - - if keys.is_empty() { - keys = rustls_pemfile::ec_private_keys(&mut Cursor::new(&tls_client_private_key)) - .map(|x| x.map(PrivateKeyDer::from)) - .collect::>() - .map_err(|err| zerror!("Error processing client key: {err}."))?; - } - - if keys.is_empty() { - bail!("No private key found for TLS client."); - } - - let builder = ClientConfig::builder_with_protocol_versions(&[&TLS13]); - - if tls_server_name_verification { - builder - .with_root_certificates(root_cert_store) - .with_client_auth_cert(certs, keys.remove(0)) - } else { - builder - .dangerous() - .with_custom_certificate_verifier(Arc::new(WebPkiVerifierAnyServerName::new( - root_cert_store, - ))) - .with_client_auth_cert(certs, keys.remove(0)) - } - .map_err(|e| zerror!("Bad certificate/key: {}", e))? - } else { - let builder = ClientConfig::builder(); - if tls_server_name_verification { - builder - .with_root_certificates(root_cert_store) - .with_no_client_auth() - } else { - builder - .dangerous() - .with_custom_certificate_verifier(Arc::new(WebPkiVerifierAnyServerName::new( - root_cert_store, - ))) - .with_no_client_auth() - } - }; - Ok(TlsClientConfig { client_config: cc }) - } - - async fn load_tls_private_key(config: &endpoint::Config<'_>) -> ZResult> { - load_tls_key( - config, - TLS_CLIENT_PRIVATE_KEY_RAW, - TLS_CLIENT_PRIVATE_KEY_FILE, - TLS_CLIENT_PRIVATE_KEY_BASE64, - ) - .await - } - - async fn load_tls_certificate(config: &endpoint::Config<'_>) -> ZResult> { - load_tls_certificate( - config, - TLS_CLIENT_CERTIFICATE_RAW, - TLS_CLIENT_CERTIFICATE_FILE, - TLS_CLIENT_CERTIFICATE_BASE64, - ) - .await - } -} - -async fn load_tls_key( - config: &endpoint::Config<'_>, - tls_private_key_raw_config_key: &str, - tls_private_key_file_config_key: &str, - tls_private_key_base64_config_key: &str, -) -> ZResult> { - if let Some(value) = config.get(tls_private_key_raw_config_key) { - return Ok(value.as_bytes().to_vec()); - } - - if let Some(b64_key) = config.get(tls_private_key_base64_config_key) { - return base64_decode(b64_key); - } - - if let Some(value) = config.get(tls_private_key_file_config_key) { - return Ok(tokio::fs::read(value) - .await - .map_err(|e| zerror!("Invalid TLS private key file: {}", e))?) - .and_then(|result| { - if result.is_empty() { - Err(zerror!("Empty TLS key.").into()) - } else { - Ok(result) - } - }); - } - Err(zerror!("Missing TLS private key.").into()) -} - -async fn load_tls_certificate( - config: &endpoint::Config<'_>, - tls_certificate_raw_config_key: &str, - tls_certificate_file_config_key: &str, - tls_certificate_base64_config_key: &str, -) -> ZResult> { - if let Some(value) = config.get(tls_certificate_raw_config_key) { - return Ok(value.as_bytes().to_vec()); - } - - if let Some(b64_certificate) = config.get(tls_certificate_base64_config_key) { - return base64_decode(b64_certificate); - } - - if let Some(value) = config.get(tls_certificate_file_config_key) { - return Ok(tokio::fs::read(value) - .await - .map_err(|e| zerror!("Invalid TLS certificate file: {}", e))?); - } - Err(zerror!("Missing tls certificates.").into()) -} - -fn load_trust_anchors(config: &endpoint::Config<'_>) -> ZResult> { - let mut root_cert_store = RootCertStore::empty(); - if let Some(value) = config.get(TLS_ROOT_CA_CERTIFICATE_RAW) { - let mut pem = BufReader::new(value.as_bytes()); - let trust_anchors = process_pem(&mut pem)?; - root_cert_store.extend(trust_anchors); - return Ok(Some(root_cert_store)); - } - - if let Some(b64_certificate) = config.get(TLS_ROOT_CA_CERTIFICATE_BASE64) { - let certificate_pem = base64_decode(b64_certificate)?; - let mut pem = BufReader::new(certificate_pem.as_slice()); - let trust_anchors = process_pem(&mut pem)?; - root_cert_store.extend(trust_anchors); - return Ok(Some(root_cert_store)); - } - - if let Some(filename) = config.get(TLS_ROOT_CA_CERTIFICATE_FILE) { - let mut pem = BufReader::new(File::open(filename)?); - let trust_anchors = process_pem(&mut pem)?; - root_cert_store.extend(trust_anchors); - return Ok(Some(root_cert_store)); - } - Ok(None) -} - -fn process_pem(pem: &mut dyn BufRead) -> ZResult>> { - let certs: Vec = rustls_pemfile::certs(pem) - .map(|result| result.map_err(|err| zerror!("Error processing PEM certificates: {err}."))) - .collect::, ZError>>()?; - - let trust_anchors: Vec = certs - .into_iter() - .map(|cert| { - anchor_from_trusted_cert(&cert) - .map_err(|err| zerror!("Error processing trust anchor: {err}.")) - .map(|trust_anchor| trust_anchor.to_owned()) - }) - .collect::, ZError>>()?; - - Ok(trust_anchors) -} diff --git a/io/zenoh-link/src/lib.rs b/io/zenoh-link/src/lib.rs index a37eabaef8..21f26ecf1b 100644 --- a/io/zenoh-link/src/lib.rs +++ b/io/zenoh-link/src/lib.rs @@ -19,9 +19,6 @@ //! [Click here for Zenoh's documentation](../zenoh/index.html) use std::collections::HashMap; use zenoh_config::Config; - -#[cfg(any(feature = "transport_quic", feature = "transport_tls"))] -use zenoh_link_commons::tls::TlsConfigurator; use zenoh_result::{bail, ZResult}; #[cfg(feature = "transport_tcp")] @@ -39,12 +36,16 @@ use zenoh_link_udp::{ #[cfg(feature = "transport_tls")] pub use zenoh_link_tls as tls; #[cfg(feature = "transport_tls")] -use zenoh_link_tls::{LinkManagerUnicastTls, TlsLocatorInspector, TLS_LOCATOR_PREFIX}; +use zenoh_link_tls::{ + LinkManagerUnicastTls, TlsConfigurator, TlsLocatorInspector, TLS_LOCATOR_PREFIX, +}; #[cfg(feature = "transport_quic")] pub use zenoh_link_quic as quic; #[cfg(feature = "transport_quic")] -use zenoh_link_quic::{LinkManagerUnicastQuic, QuicLocatorInspector, QUIC_LOCATOR_PREFIX}; +use zenoh_link_quic::{ + LinkManagerUnicastQuic, QuicConfigurator, QuicLocatorInspector, QUIC_LOCATOR_PREFIX, +}; #[cfg(feature = "transport_ws")] pub use zenoh_link_ws as ws; @@ -154,7 +155,7 @@ impl LocatorInspector { #[derive(Default)] pub struct LinkConfigurator { #[cfg(feature = "transport_quic")] - quic_inspector: TlsConfigurator, + quic_inspector: QuicConfigurator, #[cfg(feature = "transport_tls")] tls_inspector: TlsConfigurator, #[cfg(feature = "transport_unixpipe")] diff --git a/io/zenoh-links/zenoh-link-quic/Cargo.toml b/io/zenoh-links/zenoh-link-quic/Cargo.toml index 84c4b035a2..47fbd35cb7 100644 --- a/io/zenoh-links/zenoh-link-quic/Cargo.toml +++ b/io/zenoh-links/zenoh-link-quic/Cargo.toml @@ -12,35 +12,46 @@ # ZettaScale Zenoh Team, # [package] -rust-version = { workspace = true } -name = "zenoh-link-quic" -version = { workspace = true } -repository = { workspace = true } -homepage = { workspace = true } authors = { workspace = true } -edition = { workspace = true } -license = { workspace = true } categories = { workspace = true } description = "Internal crate for zenoh." +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +name = "zenoh-link-quic" +repository = { workspace = true } +rust-version = { workspace = true } +version = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] async-trait = { workspace = true } base64 = { workspace = true } futures = { workspace = true } -tracing = {workspace = true} quinn = { workspace = true } -tokio = { workspace = true, features = ["io-util", "net", "fs", "sync", "time"] } +rustls-native-certs = { workspace = true } +rustls-pemfile = { version = "1" } +rustls-pki-types = { version = "1.0" } +rustls-webpki = { workspace = true } +secrecy = { workspace = true } +tokio = { workspace = true, features = [ + "fs", + "io-util", + "net", + "sync", + "time", +] } tokio-util = { workspace = true, features = ["rt"] } +tracing = { workspace = true } +webpki-roots = { workspace = true } zenoh-config = { workspace = true } zenoh-core = { workspace = true } -zenoh-link-commons = { workspace = true, features = ["tls"]} +zenoh-link-commons = { workspace = true } zenoh-protocol = { workspace = true } zenoh-result = { workspace = true } +zenoh-runtime = { workspace = true } zenoh-sync = { workspace = true } zenoh-util = { workspace = true } -zenoh-runtime = { workspace = true } - # Lock due to quinn not supporting rustls 0.22 yet rustls = { version = "0.21", features = ["dangerous_configuration", "quic"] } tokio-rustls = "0.24.1" diff --git a/io/zenoh-links/zenoh-link-quic/src/lib.rs b/io/zenoh-links/zenoh-link-quic/src/lib.rs index 113f11ac78..4b57ea88ca 100644 --- a/io/zenoh-links/zenoh-link-quic/src/lib.rs +++ b/io/zenoh-links/zenoh-link-quic/src/lib.rs @@ -19,15 +19,16 @@ //! [Click here for Zenoh's documentation](../zenoh/index.html) use async_trait::async_trait; -use std::net::SocketAddr; use zenoh_core::zconfigurable; use zenoh_link_commons::LocatorInspector; -use zenoh_protocol::core::{endpoint::Address, Locator}; -use zenoh_result::{bail, zerror, ZResult}; +use zenoh_protocol::core::Locator; +use zenoh_result::ZResult; mod unicast; +mod utils; mod verify; pub use unicast::*; +pub use utils::TlsConfigurator as QuicConfigurator; // Default ALPN protocol pub const ALPN_QUIC_HTTP: &[&[u8]] = &[b"hq-29"]; @@ -68,17 +69,29 @@ zconfigurable! { static ref QUIC_ACCEPT_THROTTLE_TIME: u64 = 100_000; } -async fn get_quic_addr(address: &Address<'_>) -> ZResult { - match tokio::net::lookup_host(address.as_str()).await?.next() { - Some(addr) => Ok(addr), - None => bail!("Couldn't resolve QUIC locator address: {}", address), - } -} +pub mod config { + pub const TLS_ROOT_CA_CERTIFICATE_FILE: &str = "root_ca_certificate_file"; + pub const TLS_ROOT_CA_CERTIFICATE_RAW: &str = "root_ca_certificate_raw"; + pub const TLS_ROOT_CA_CERTIFICATE_BASE64: &str = "root_ca_certificate_base64"; + + pub const TLS_SERVER_PRIVATE_KEY_FILE: &str = "server_private_key_file"; + pub const TLS_SERVER_PRIVATE_KEY_RAW: &str = "server_private_key_raw"; + pub const TLS_SERVER_PRIVATE_KEY_BASE_64: &str = "server_private_key_base64"; + + pub const TLS_SERVER_CERTIFICATE_FILE: &str = "server_certificate_file"; + pub const TLS_SERVER_CERTIFICATE_RAW: &str = "server_certificate_raw"; + pub const TLS_SERVER_CERTIFICATE_BASE64: &str = "server_certificate_base64"; + + pub const TLS_CLIENT_PRIVATE_KEY_FILE: &str = "client_private_key_file"; + pub const TLS_CLIENT_PRIVATE_KEY_RAW: &str = "client_private_key_raw"; + pub const TLS_CLIENT_PRIVATE_KEY_BASE64: &str = "client_private_key_base64"; + + pub const TLS_CLIENT_CERTIFICATE_FILE: &str = "client_certificate_file"; + pub const TLS_CLIENT_CERTIFICATE_RAW: &str = "client_certificate_raw"; + pub const TLS_CLIENT_CERTIFICATE_BASE64: &str = "client_certificate_base64"; + + pub const TLS_CLIENT_AUTH: &str = "client_auth"; -pub fn base64_decode(data: &str) -> ZResult> { - use base64::engine::general_purpose; - use base64::Engine; - Ok(general_purpose::STANDARD - .decode(data) - .map_err(|e| zerror!("Unable to perform base64 decoding: {e:?}"))?) + pub const TLS_SERVER_NAME_VERIFICATION: &str = "server_name_verification"; + pub const TLS_SERVER_NAME_VERIFICATION_DEFAULT: &str = "true"; } diff --git a/io/zenoh-links/zenoh-link-quic/src/unicast.rs b/io/zenoh-links/zenoh-link-quic/src/unicast.rs index a023c74b38..ec9e54a0bf 100644 --- a/io/zenoh-links/zenoh-link-quic/src/unicast.rs +++ b/io/zenoh-links/zenoh-link-quic/src/unicast.rs @@ -12,16 +12,13 @@ // ZettaScale Zenoh Team, // -use crate::base64_decode; use crate::{ - get_quic_addr, verify::WebPkiVerifierAnyServerName, ALPN_QUIC_HTTP, QUIC_ACCEPT_THROTTLE_TIME, - QUIC_DEFAULT_MTU, QUIC_LOCATOR_PREFIX, + config::*, + utils::{get_quic_addr, TlsClientConfig, TlsServerConfig}, + ALPN_QUIC_HTTP, QUIC_ACCEPT_THROTTLE_TIME, QUIC_DEFAULT_MTU, QUIC_LOCATOR_PREFIX, }; use async_trait::async_trait; -use rustls::{Certificate, PrivateKey}; -use rustls_pemfile::Item; use std::fmt; -use std::io::BufReader; use std::net::IpAddr; use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr}; use std::sync::Arc; @@ -29,19 +26,13 @@ use std::time::Duration; use tokio::sync::Mutex as AsyncMutex; use tokio_util::sync::CancellationToken; use zenoh_core::zasynclock; -use zenoh_link_commons::tls::config::{ - TLS_ROOT_CA_CERTIFICATE_BASE64, TLS_ROOT_CA_CERTIFICATE_FILE, TLS_ROOT_CA_CERTIFICATE_RAW, - TLS_SERVER_CERTIFICATE_BASE64, TLS_SERVER_CERTIFICATE_FILE, TLS_SERVER_CERTIFICATE_RAW, - TLS_SERVER_NAME_VERIFICATION, TLS_SERVER_NAME_VERIFICATION_DEFAULT, - TLS_SERVER_PRIVATE_KEY_FILE, TLS_SERVER_PRIVATE_KEY_RAW, -}; -use zenoh_link_commons::tls::{TlsClientConfig, TlsServerConfig}; + use zenoh_link_commons::{ get_ip_interface_names, LinkManagerUnicastTrait, LinkUnicast, LinkUnicastTrait, ListenersUnicastIP, NewLinkChannelSender, }; use zenoh_protocol::core::{EndPoint, Locator}; -use zenoh_result::{bail, zerror, ZError, ZResult}; +use zenoh_result::{bail, zerror, ZResult}; pub struct LinkUnicastQuic { connection: quinn::Connection, diff --git a/io/zenoh-links/zenoh-link-quic/src/utils.rs b/io/zenoh-links/zenoh-link-quic/src/utils.rs new file mode 100644 index 0000000000..f7c06dc193 --- /dev/null +++ b/io/zenoh-links/zenoh-link-quic/src/utils.rs @@ -0,0 +1,509 @@ +// +// Copyright (c) 2024 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::config::*; +use crate::verify::WebPkiVerifierAnyServerName; +use rustls::OwnedTrustAnchor; +use rustls::{ + server::AllowAnyAuthenticatedClient, version::TLS13, Certificate, ClientConfig, PrivateKey, + RootCertStore, ServerConfig, +}; +use rustls_pki_types::{CertificateDer, TrustAnchor}; +use secrecy::ExposeSecret; +use zenoh_link_commons::ConfigurationInspector; +// use rustls_pki_types::{CertificateDer, PrivateKeyDer, TrustAnchor}; +use std::fs::File; +use std::io; +use std::net::SocketAddr; +use std::{ + io::{BufReader, Cursor}, + sync::Arc, +}; +use webpki::anchor_from_trusted_cert; +use zenoh_config::Config as ZenohConfig; +use zenoh_protocol::core::endpoint::Config; +use zenoh_protocol::core::endpoint::{self, Address}; +use zenoh_result::{bail, zerror, ZError, ZResult}; + +#[derive(Default, Clone, Copy, Debug)] +pub struct TlsConfigurator; + +impl ConfigurationInspector for TlsConfigurator { + fn inspect_config(&self, config: &ZenohConfig) -> ZResult { + let mut ps: Vec<(&str, &str)> = vec![]; + + let c = config.transport().link().tls(); + + match (c.root_ca_certificate(), c.root_ca_certificate_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'root_ca_certificate' and 'root_ca_certificate_base64' can be present!") + } + (Some(ca_certificate), None) => { + ps.push((TLS_ROOT_CA_CERTIFICATE_FILE, ca_certificate)); + } + (None, Some(ca_certificate)) => { + ps.push(( + TLS_ROOT_CA_CERTIFICATE_BASE64, + ca_certificate.expose_secret(), + )); + } + _ => {} + } + + match (c.server_private_key(), c.server_private_key_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'server_private_key' and 'server_private_key_base64' can be present!") + } + (Some(server_private_key), None) => { + ps.push((TLS_SERVER_PRIVATE_KEY_FILE, server_private_key)); + } + (None, Some(server_private_key)) => { + ps.push(( + TLS_SERVER_PRIVATE_KEY_BASE_64, + server_private_key.expose_secret(), + )); + } + _ => {} + } + + match (c.server_certificate(), c.server_certificate_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'server_certificate' and 'server_certificate_base64' can be present!") + } + (Some(server_certificate), None) => { + ps.push((TLS_SERVER_CERTIFICATE_FILE, server_certificate)); + } + (None, Some(server_certificate)) => { + ps.push(( + TLS_SERVER_CERTIFICATE_BASE64, + server_certificate.expose_secret(), + )); + } + _ => {} + } + + if let Some(client_auth) = c.client_auth() { + match client_auth { + true => ps.push((TLS_CLIENT_AUTH, "true")), + false => ps.push((TLS_CLIENT_AUTH, "false")), + }; + } + + match (c.client_private_key(), c.client_private_key_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'client_private_key' and 'client_private_key_base64' can be present!") + } + (Some(client_private_key), None) => { + ps.push((TLS_CLIENT_PRIVATE_KEY_FILE, client_private_key)); + } + (None, Some(client_private_key)) => { + ps.push(( + TLS_CLIENT_PRIVATE_KEY_BASE64, + client_private_key.expose_secret(), + )); + } + _ => {} + } + + match (c.client_certificate(), c.client_certificate_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'client_certificate' and 'client_certificate_base64' can be present!") + } + (Some(client_certificate), None) => { + ps.push((TLS_CLIENT_CERTIFICATE_FILE, client_certificate)); + } + (None, Some(client_certificate)) => { + ps.push(( + TLS_CLIENT_CERTIFICATE_BASE64, + client_certificate.expose_secret(), + )); + } + _ => {} + } + + if let Some(server_name_verification) = c.server_name_verification() { + match server_name_verification { + true => ps.push((TLS_SERVER_NAME_VERIFICATION, "true")), + false => ps.push((TLS_SERVER_NAME_VERIFICATION, "false")), + }; + } + + let mut s = String::new(); + endpoint::Parameters::extend(ps.drain(..), &mut s); + + Ok(s) + } +} + +pub(crate) struct TlsServerConfig { + pub(crate) server_config: ServerConfig, +} + +impl TlsServerConfig { + pub async fn new(config: &Config<'_>) -> ZResult { + let tls_server_client_auth: bool = match config.get(TLS_CLIENT_AUTH) { + Some(s) => s + .parse() + .map_err(|_| zerror!("Unknown client auth argument: {}", s))?, + None => false, + }; + let tls_server_private_key = TlsServerConfig::load_tls_private_key(config).await?; + let tls_server_certificate = TlsServerConfig::load_tls_certificate(config).await?; + + let certs: Vec = + rustls_pemfile::certs(&mut Cursor::new(&tls_server_certificate)) + .map_err(|err| zerror!("Error processing server certificate: {err}."))? + .into_iter() + .map(Certificate) + .collect(); + + let mut keys: Vec = + rustls_pemfile::rsa_private_keys(&mut Cursor::new(&tls_server_private_key)) + .map_err(|err| zerror!("Error processing server key: {err}."))? + .into_iter() + .map(PrivateKey) + .collect(); + + if keys.is_empty() { + keys = rustls_pemfile::pkcs8_private_keys(&mut Cursor::new(&tls_server_private_key)) + .map_err(|err| zerror!("Error processing server key: {err}."))? + .into_iter() + .map(PrivateKey) + .collect(); + } + + if keys.is_empty() { + keys = rustls_pemfile::ec_private_keys(&mut Cursor::new(&tls_server_private_key)) + .map_err(|err| zerror!("Error processing server key: {err}."))? + .into_iter() + .map(PrivateKey) + .collect(); + } + + if keys.is_empty() { + bail!("No private key found for TLS server."); + } + + let sc = if tls_server_client_auth { + let root_cert_store = load_trust_anchors(config)?.map_or_else( + || { + Err(zerror!( + "Missing root certificates while client authentication is enabled." + )) + }, + Ok, + )?; + let client_auth = AllowAnyAuthenticatedClient::new(root_cert_store); + ServerConfig::builder() + .with_safe_default_cipher_suites() + .with_safe_default_kx_groups() + .with_protocol_versions(&[&TLS13])? + .with_client_cert_verifier(Arc::new(client_auth)) + .with_single_cert(certs, keys.remove(0)) + .map_err(|e| zerror!(e))? + } else { + ServerConfig::builder() + .with_safe_defaults() + .with_no_client_auth() + .with_single_cert(certs, keys.remove(0)) + .map_err(|e| zerror!(e))? + }; + Ok(TlsServerConfig { server_config: sc }) + } + + async fn load_tls_private_key(config: &Config<'_>) -> ZResult> { + load_tls_key( + config, + TLS_SERVER_PRIVATE_KEY_RAW, + TLS_SERVER_PRIVATE_KEY_FILE, + TLS_SERVER_PRIVATE_KEY_BASE_64, + ) + .await + } + + async fn load_tls_certificate(config: &Config<'_>) -> ZResult> { + load_tls_certificate( + config, + TLS_SERVER_CERTIFICATE_RAW, + TLS_SERVER_CERTIFICATE_FILE, + TLS_SERVER_CERTIFICATE_BASE64, + ) + .await + } +} + +pub(crate) struct TlsClientConfig { + pub(crate) client_config: ClientConfig, +} + +impl TlsClientConfig { + pub async fn new(config: &Config<'_>) -> ZResult { + let tls_client_server_auth: bool = match config.get(TLS_CLIENT_AUTH) { + Some(s) => s + .parse() + .map_err(|_| zerror!("Unknown client auth argument: {}", s))?, + None => false, + }; + + let tls_server_name_verification: bool = match config.get(TLS_SERVER_NAME_VERIFICATION) { + Some(s) => { + let s: bool = s + .parse() + .map_err(|_| zerror!("Unknown server name verification argument: {}", s))?; + if s { + tracing::warn!("Skipping name verification of servers"); + } + s + } + None => false, + }; + + // Allows mixed user-generated CA and webPKI CA + tracing::debug!("Loading default Web PKI certificates."); + let mut root_cert_store = RootCertStore { + roots: webpki_roots::TLS_SERVER_ROOTS + .iter() + .map(|ta| ta.to_owned()) + .map(|ta| { + OwnedTrustAnchor::from_subject_spki_name_constraints( + ta.subject.to_vec(), + ta.subject_public_key_info.to_vec(), + ta.name_constraints.map(|nc| nc.to_vec()), + ) + }) + .collect(), + }; + + if let Some(custom_root_cert) = load_trust_anchors(config)? { + tracing::debug!("Loading user-generated certificates."); + root_cert_store.roots.extend(custom_root_cert.roots); + } + + let cc = if tls_client_server_auth { + tracing::debug!("Loading client authentication key and certificate..."); + let tls_client_private_key = TlsClientConfig::load_tls_private_key(config).await?; + let tls_client_certificate = TlsClientConfig::load_tls_certificate(config).await?; + + let certs: Vec = + rustls_pemfile::certs(&mut Cursor::new(&tls_client_certificate)) + .map_err(|err| zerror!("Error processing client certificate: {err}."))? + .into_iter() + .map(Certificate) + .collect(); + + let mut keys: Vec = + rustls_pemfile::rsa_private_keys(&mut Cursor::new(&tls_client_private_key)) + .map_err(|err| zerror!("Error processing client key: {err}."))? + .into_iter() + .map(PrivateKey) + .collect(); + + if keys.is_empty() { + keys = + rustls_pemfile::pkcs8_private_keys(&mut Cursor::new(&tls_client_private_key)) + .map_err(|err| zerror!("Error processing client key: {err}."))? + .into_iter() + .map(PrivateKey) + .collect(); + } + + if keys.is_empty() { + keys = rustls_pemfile::ec_private_keys(&mut Cursor::new(&tls_client_private_key)) + .map_err(|err| zerror!("Error processing client key: {err}."))? + .into_iter() + .map(PrivateKey) + .collect(); + } + + if keys.is_empty() { + bail!("No private key found for TLS client."); + } + + let builder = ClientConfig::builder() + .with_safe_default_cipher_suites() + .with_safe_default_kx_groups() + .with_protocol_versions(&[&TLS13])?; + + if tls_server_name_verification { + builder + .with_root_certificates(root_cert_store) + .with_client_auth_cert(certs, keys.remove(0)) + } else { + builder + .with_custom_certificate_verifier(Arc::new(WebPkiVerifierAnyServerName::new( + root_cert_store, + ))) + .with_client_auth_cert(certs, keys.remove(0)) + } + .map_err(|e| zerror!("Bad certificate/key: {}", e))? + } else { + let builder = ClientConfig::builder() + .with_safe_default_cipher_suites() + .with_safe_default_kx_groups() + .with_protocol_versions(&[&TLS13])?; + + if tls_server_name_verification { + builder + .with_root_certificates(root_cert_store) + .with_no_client_auth() + } else { + builder + .with_custom_certificate_verifier(Arc::new(WebPkiVerifierAnyServerName::new( + root_cert_store, + ))) + .with_no_client_auth() + } + }; + Ok(TlsClientConfig { client_config: cc }) + } + + async fn load_tls_private_key(config: &Config<'_>) -> ZResult> { + load_tls_key( + config, + TLS_CLIENT_PRIVATE_KEY_RAW, + TLS_CLIENT_PRIVATE_KEY_FILE, + TLS_CLIENT_PRIVATE_KEY_BASE64, + ) + .await + } + + async fn load_tls_certificate(config: &Config<'_>) -> ZResult> { + load_tls_certificate( + config, + TLS_CLIENT_CERTIFICATE_RAW, + TLS_CLIENT_CERTIFICATE_FILE, + TLS_CLIENT_CERTIFICATE_BASE64, + ) + .await + } +} + +fn process_pem(pem: &mut dyn io::BufRead) -> ZResult> { + let certs: Vec = rustls_pemfile::certs(pem) + .map_err(|err| zerror!("Error processing PEM certificates: {err}."))? + .into_iter() + .map(CertificateDer::from) + .collect(); + + let trust_anchors: Vec = certs + .into_iter() + .map(|cert| { + anchor_from_trusted_cert(&cert) + .map_err(|err| zerror!("Error processing trust anchor: {err}.")) + .map(|trust_anchor| trust_anchor.to_owned()) + }) + .collect::, ZError>>()? + .into_iter() + .map(|ta| { + OwnedTrustAnchor::from_subject_spki_name_constraints( + ta.subject.to_vec(), + ta.subject_public_key_info.to_vec(), + ta.name_constraints.map(|nc| nc.to_vec()), + ) + }) + .collect(); + + Ok(trust_anchors) +} + +async fn load_tls_key( + config: &Config<'_>, + tls_private_key_raw_config_key: &str, + tls_private_key_file_config_key: &str, + tls_private_key_base64_config_key: &str, +) -> ZResult> { + if let Some(value) = config.get(tls_private_key_raw_config_key) { + return Ok(value.as_bytes().to_vec()); + } + + if let Some(b64_key) = config.get(tls_private_key_base64_config_key) { + return base64_decode(b64_key); + } + + if let Some(value) = config.get(tls_private_key_file_config_key) { + return Ok(tokio::fs::read(value) + .await + .map_err(|e| zerror!("Invalid TLS private key file: {}", e))?) + .and_then(|result| { + if result.is_empty() { + Err(zerror!("Empty TLS key.").into()) + } else { + Ok(result) + } + }); + } + Err(zerror!("Missing TLS private key.").into()) +} + +async fn load_tls_certificate( + config: &Config<'_>, + tls_certificate_raw_config_key: &str, + tls_certificate_file_config_key: &str, + tls_certificate_base64_config_key: &str, +) -> ZResult> { + if let Some(value) = config.get(tls_certificate_raw_config_key) { + return Ok(value.as_bytes().to_vec()); + } + + if let Some(b64_certificate) = config.get(tls_certificate_base64_config_key) { + return base64_decode(b64_certificate); + } + + if let Some(value) = config.get(tls_certificate_file_config_key) { + return Ok(tokio::fs::read(value) + .await + .map_err(|e| zerror!("Invalid TLS certificate file: {}", e))?); + } + Err(zerror!("Missing tls certificates.").into()) +} + +fn load_trust_anchors(config: &Config<'_>) -> ZResult> { + let mut root_cert_store = RootCertStore::empty(); + if let Some(value) = config.get(TLS_ROOT_CA_CERTIFICATE_RAW) { + let mut pem = BufReader::new(value.as_bytes()); + let trust_anchors = process_pem(&mut pem)?; + root_cert_store.roots.extend(trust_anchors); + return Ok(Some(root_cert_store)); + } + + if let Some(b64_certificate) = config.get(TLS_ROOT_CA_CERTIFICATE_BASE64) { + let certificate_pem = base64_decode(b64_certificate)?; + let mut pem = BufReader::new(certificate_pem.as_slice()); + let trust_anchors = process_pem(&mut pem)?; + root_cert_store.roots.extend(trust_anchors); + return Ok(Some(root_cert_store)); + } + + if let Some(filename) = config.get(TLS_ROOT_CA_CERTIFICATE_FILE) { + let mut pem = BufReader::new(File::open(filename)?); + let trust_anchors = process_pem(&mut pem)?; + root_cert_store.roots.extend(trust_anchors); + return Ok(Some(root_cert_store)); + } + Ok(None) +} + +pub async fn get_quic_addr(address: &Address<'_>) -> ZResult { + match tokio::net::lookup_host(address.as_str()).await?.next() { + Some(addr) => Ok(addr), + None => bail!("Couldn't resolve QUIC locator address: {}", address), + } +} + +pub fn base64_decode(data: &str) -> ZResult> { + use base64::engine::general_purpose; + use base64::Engine; + Ok(general_purpose::STANDARD + .decode(data) + .map_err(|e| zerror!("Unable to perform base64 decoding: {e:?}"))?) +} diff --git a/io/zenoh-links/zenoh-link-tls/Cargo.toml b/io/zenoh-links/zenoh-link-tls/Cargo.toml index dbf63ed157..91fb72787e 100644 --- a/io/zenoh-links/zenoh-link-tls/Cargo.toml +++ b/io/zenoh-links/zenoh-link-tls/Cargo.toml @@ -12,34 +12,35 @@ # ZettaScale Zenoh Team, # [package] -rust-version = { workspace = true } -name = "zenoh-link-tls" -version = { workspace = true } -repository = { workspace = true } -homepage = { workspace = true } authors = { workspace = true } -edition = { workspace = true } -license = { workspace = true } categories = { workspace = true } description = "Internal crate for zenoh." +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +name = "zenoh-link-tls" +repository = { workspace = true } +rust-version = { workspace = true } +version = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] async-trait = { workspace = true } base64 = { workspace = true } futures = { workspace = true } -tracing = {workspace = true} rustls = { workspace = true } rustls-pemfile = { workspace = true } rustls-pki-types = { workspace = true } rustls-webpki = { workspace = true } -secrecy = {workspace = true } -tokio = { workspace = true, features = ["io-util", "net", "fs", "sync"] } +secrecy = { workspace = true } +tokio = { workspace = true, features = ["fs", "io-util", "net", "sync"] } tokio-rustls = { workspace = true } tokio-util = { workspace = true, features = ["rt"] } +tracing = { workspace = true } +webpki-roots = { workspace = true } zenoh-config = { workspace = true } zenoh-core = { workspace = true } -zenoh-link-commons = { workspace = true, features = ["tls"]} +zenoh-link-commons = { workspace = true } zenoh-protocol = { workspace = true } zenoh-result = { workspace = true } zenoh-runtime = { workspace = true } diff --git a/io/zenoh-links/zenoh-link-tls/src/lib.rs b/io/zenoh-links/zenoh-link-tls/src/lib.rs index 460bea400b..b9002cc397 100644 --- a/io/zenoh-links/zenoh-link-tls/src/lib.rs +++ b/io/zenoh-links/zenoh-link-tls/src/lib.rs @@ -18,15 +18,15 @@ //! //! [Click here for Zenoh's documentation](../zenoh/index.html) use async_trait::async_trait; -use rustls_pki_types::ServerName; -use std::{convert::TryFrom, net::SocketAddr}; use zenoh_core::zconfigurable; use zenoh_link_commons::LocatorInspector; -use zenoh_protocol::core::{endpoint::Address, Locator}; -use zenoh_result::{bail, zerror, ZResult}; +use zenoh_protocol::core::Locator; +use zenoh_result::ZResult; mod unicast; +mod utils; pub use unicast::*; +pub use utils::TlsConfigurator; // Default MTU (TLS PDU) in bytes. // NOTE: Since TLS is a byte-stream oriented transport, theoretically it has @@ -63,29 +63,28 @@ zconfigurable! { static ref TLS_ACCEPT_THROTTLE_TIME: u64 = 100_000; } -pub async fn get_tls_addr(address: &Address<'_>) -> ZResult { - match tokio::net::lookup_host(address.as_str()).await?.next() { - Some(addr) => Ok(addr), - None => bail!("Couldn't resolve TLS locator address: {}", address), - } -} +pub mod config { + pub const TLS_ROOT_CA_CERTIFICATE_FILE: &str = "root_ca_certificate_file"; + pub const TLS_ROOT_CA_CERTIFICATE_RAW: &str = "root_ca_certificate_raw"; + pub const TLS_ROOT_CA_CERTIFICATE_BASE64: &str = "root_ca_certificate_base64"; -pub fn get_tls_host<'a>(address: &'a Address<'a>) -> ZResult<&'a str> { - address - .as_str() - .split(':') - .next() - .ok_or_else(|| zerror!("Invalid TLS address").into()) -} + pub const TLS_SERVER_PRIVATE_KEY_FILE: &str = "server_private_key_file"; + pub const TLS_SERVER_PRIVATE_KEY_RAW: &str = "server_private_key_raw"; + pub const TLS_SERVER_PRIVATE_KEY_BASE_64: &str = "server_private_key_base64"; -pub fn get_tls_server_name<'a>(address: &'a Address<'a>) -> ZResult> { - Ok(ServerName::try_from(get_tls_host(address)?).map_err(|e| zerror!(e))?) -} + pub const TLS_SERVER_CERTIFICATE_FILE: &str = "server_certificate_file"; + pub const TLS_SERVER_CERTIFICATE_RAW: &str = "server_certificate_raw"; + pub const TLS_SERVER_CERTIFICATE_BASE64: &str = "server_certificate_base64"; + + pub const TLS_CLIENT_PRIVATE_KEY_FILE: &str = "client_private_key_file"; + pub const TLS_CLIENT_PRIVATE_KEY_RAW: &str = "client_private_key_raw"; + pub const TLS_CLIENT_PRIVATE_KEY_BASE64: &str = "client_private_key_base64"; + + pub const TLS_CLIENT_CERTIFICATE_FILE: &str = "client_certificate_file"; + pub const TLS_CLIENT_CERTIFICATE_RAW: &str = "client_certificate_raw"; + pub const TLS_CLIENT_CERTIFICATE_BASE64: &str = "client_certificate_base64"; + + pub const TLS_CLIENT_AUTH: &str = "client_auth"; -pub fn base64_decode(data: &str) -> ZResult> { - use base64::engine::general_purpose; - use base64::Engine; - Ok(general_purpose::STANDARD - .decode(data) - .map_err(|e| zerror!("Unable to perform base64 decoding: {e:?}"))?) + pub const TLS_SERVER_NAME_VERIFICATION: &str = "server_name_verification"; } diff --git a/io/zenoh-links/zenoh-link-tls/src/unicast.rs b/io/zenoh-links/zenoh-link-tls/src/unicast.rs index 742929941c..b12608354e 100644 --- a/io/zenoh-links/zenoh-link-tls/src/unicast.rs +++ b/io/zenoh-links/zenoh-link-tls/src/unicast.rs @@ -12,49 +12,29 @@ // ZettaScale Zenoh Team, // use crate::{ - base64_decode, get_tls_addr, get_tls_host, get_tls_server_name, TLS_ACCEPT_THROTTLE_TIME, - TLS_DEFAULT_MTU, TLS_LINGER_TIMEOUT, TLS_LOCATOR_PREFIX, + utils::{get_tls_addr, get_tls_host, get_tls_server_name, TlsClientConfig, TlsServerConfig}, + TLS_ACCEPT_THROTTLE_TIME, TLS_DEFAULT_MTU, TLS_LINGER_TIMEOUT, TLS_LOCATOR_PREFIX, }; + use async_trait::async_trait; -use rustls::{ - pki_types::{CertificateDer, PrivateKeyDer, TrustAnchor}, - server::WebPkiClientVerifier, - version::TLS13, - ClientConfig, RootCertStore, ServerConfig, -}; +use std::cell::UnsafeCell; use std::convert::TryInto; use std::fmt; -use std::fs::File; -use std::io::{BufReader, Cursor}; use std::net::SocketAddr; use std::sync::Arc; use std::time::Duration; -use std::{cell::UnsafeCell, io}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::{TcpListener, TcpStream}; use tokio::sync::Mutex as AsyncMutex; use tokio_rustls::{TlsAcceptor, TlsConnector, TlsStream}; use tokio_util::sync::CancellationToken; -use webpki::anchor_from_trusted_cert; use zenoh_core::zasynclock; -use zenoh_link_commons::tls::{ - config::{ - TLS_CLIENT_AUTH, TLS_CLIENT_CERTIFICATE_BASE64, TLS_CLIENT_CERTIFICATE_FILE, - TLS_CLIENT_CERTIFICATE_RAW, TLS_CLIENT_PRIVATE_KEY_BASE64, TLS_CLIENT_PRIVATE_KEY_FILE, - TLS_CLIENT_PRIVATE_KEY_RAW, TLS_ROOT_CA_CERTIFICATE_BASE64, TLS_ROOT_CA_CERTIFICATE_FILE, - TLS_ROOT_CA_CERTIFICATE_RAW, TLS_SERVER_CERTIFICATE_BASE64, TLS_SERVER_CERTIFICATE_FILE, - TLS_SERVER_CERTIFICATE_RAW, TLS_SERVER_NAME_VERIFICATION, TLS_SERVER_PRIVATE_KEY_BASE_64, - TLS_SERVER_PRIVATE_KEY_FILE, TLS_SERVER_PRIVATE_KEY_RAW, - }, - TlsClientConfig, TlsServerConfig, WebPkiVerifierAnyServerName, -}; use zenoh_link_commons::{ get_ip_interface_names, LinkManagerUnicastTrait, LinkUnicast, LinkUnicastTrait, ListenersUnicastIP, NewLinkChannelSender, }; -use zenoh_protocol::core::endpoint::Config; use zenoh_protocol::core::{EndPoint, Locator}; -use zenoh_result::{bail, zerror, ZError, ZResult}; +use zenoh_result::{zerror, ZResult}; pub struct LinkUnicastTls { // The underlying socket as returned from the async-rustls library diff --git a/io/zenoh-links/zenoh-link-tls/src/utils.rs b/io/zenoh-links/zenoh-link-tls/src/utils.rs new file mode 100644 index 0000000000..f62757523c --- /dev/null +++ b/io/zenoh-links/zenoh-link-tls/src/utils.rs @@ -0,0 +1,480 @@ +// +// Copyright (c) 2024 ZettaScale Technology +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License 2.0 which is available at +// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// +// Contributors: +// ZettaScale Zenoh Team, +// +use crate::config::*; +use rustls::{ + pki_types::{CertificateDer, PrivateKeyDer, TrustAnchor}, + server::WebPkiClientVerifier, + version::TLS13, + ClientConfig, RootCertStore, ServerConfig, +}; +use rustls_pki_types::ServerName; +use secrecy::ExposeSecret; +use std::fs::File; +use std::io; +use std::{convert::TryFrom, net::SocketAddr}; +use std::{ + io::{BufReader, Cursor}, + sync::Arc, +}; +use webpki::anchor_from_trusted_cert; +use zenoh_config::Config as ZenohConfig; +use zenoh_link_commons::{tls::WebPkiVerifierAnyServerName, ConfigurationInspector}; +use zenoh_protocol::core::endpoint::Config; +use zenoh_protocol::core::endpoint::{self, Address}; +use zenoh_result::{bail, zerror, ZError, ZResult}; + +#[derive(Default, Clone, Copy, Debug)] +pub struct TlsConfigurator; + +impl ConfigurationInspector for TlsConfigurator { + fn inspect_config(&self, config: &ZenohConfig) -> ZResult { + let mut ps: Vec<(&str, &str)> = vec![]; + + let c = config.transport().link().tls(); + + match (c.root_ca_certificate(), c.root_ca_certificate_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'root_ca_certificate' and 'root_ca_certificate_base64' can be present!") + } + (Some(ca_certificate), None) => { + ps.push((TLS_ROOT_CA_CERTIFICATE_FILE, ca_certificate)); + } + (None, Some(ca_certificate)) => { + ps.push(( + TLS_ROOT_CA_CERTIFICATE_BASE64, + ca_certificate.expose_secret(), + )); + } + _ => {} + } + + match (c.server_private_key(), c.server_private_key_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'server_private_key' and 'server_private_key_base64' can be present!") + } + (Some(server_private_key), None) => { + ps.push((TLS_SERVER_PRIVATE_KEY_FILE, server_private_key)); + } + (None, Some(server_private_key)) => { + ps.push(( + TLS_SERVER_PRIVATE_KEY_BASE_64, + server_private_key.expose_secret(), + )); + } + _ => {} + } + + match (c.server_certificate(), c.server_certificate_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'server_certificate' and 'server_certificate_base64' can be present!") + } + (Some(server_certificate), None) => { + ps.push((TLS_SERVER_CERTIFICATE_FILE, server_certificate)); + } + (None, Some(server_certificate)) => { + ps.push(( + TLS_SERVER_CERTIFICATE_BASE64, + server_certificate.expose_secret(), + )); + } + _ => {} + } + + if let Some(client_auth) = c.client_auth() { + match client_auth { + true => ps.push((TLS_CLIENT_AUTH, "true")), + false => ps.push((TLS_CLIENT_AUTH, "false")), + }; + } + + match (c.client_private_key(), c.client_private_key_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'client_private_key' and 'client_private_key_base64' can be present!") + } + (Some(client_private_key), None) => { + ps.push((TLS_CLIENT_PRIVATE_KEY_FILE, client_private_key)); + } + (None, Some(client_private_key)) => { + ps.push(( + TLS_CLIENT_PRIVATE_KEY_BASE64, + client_private_key.expose_secret(), + )); + } + _ => {} + } + + match (c.client_certificate(), c.client_certificate_base64()) { + (Some(_), Some(_)) => { + bail!("Only one between 'client_certificate' and 'client_certificate_base64' can be present!") + } + (Some(client_certificate), None) => { + ps.push((TLS_CLIENT_CERTIFICATE_FILE, client_certificate)); + } + (None, Some(client_certificate)) => { + ps.push(( + TLS_CLIENT_CERTIFICATE_BASE64, + client_certificate.expose_secret(), + )); + } + _ => {} + } + + if let Some(server_name_verification) = c.server_name_verification() { + match server_name_verification { + true => ps.push((TLS_SERVER_NAME_VERIFICATION, "true")), + false => ps.push((TLS_SERVER_NAME_VERIFICATION, "false")), + }; + } + + let mut s = String::new(); + endpoint::Parameters::extend(ps.drain(..), &mut s); + + Ok(s) + } +} + +pub(crate) struct TlsServerConfig { + pub(crate) server_config: ServerConfig, +} + +impl TlsServerConfig { + pub async fn new(config: &Config<'_>) -> ZResult { + let tls_server_client_auth: bool = match config.get(TLS_CLIENT_AUTH) { + Some(s) => s + .parse() + .map_err(|_| zerror!("Unknown client auth argument: {}", s))?, + None => false, + }; + let tls_server_private_key = TlsServerConfig::load_tls_private_key(config).await?; + let tls_server_certificate = TlsServerConfig::load_tls_certificate(config).await?; + + let certs: Vec = + rustls_pemfile::certs(&mut Cursor::new(&tls_server_certificate)) + .collect::>() + .map_err(|err| zerror!("Error processing server certificate: {err}."))?; + + let mut keys: Vec = + rustls_pemfile::rsa_private_keys(&mut Cursor::new(&tls_server_private_key)) + .map(|x| x.map(PrivateKeyDer::from)) + .collect::>() + .map_err(|err| zerror!("Error processing server key: {err}."))?; + + if keys.is_empty() { + keys = rustls_pemfile::pkcs8_private_keys(&mut Cursor::new(&tls_server_private_key)) + .map(|x| x.map(PrivateKeyDer::from)) + .collect::>() + .map_err(|err| zerror!("Error processing server key: {err}."))?; + } + + if keys.is_empty() { + keys = rustls_pemfile::ec_private_keys(&mut Cursor::new(&tls_server_private_key)) + .map(|x| x.map(PrivateKeyDer::from)) + .collect::>() + .map_err(|err| zerror!("Error processing server key: {err}."))?; + } + + if keys.is_empty() { + bail!("No private key found for TLS server."); + } + + let sc = if tls_server_client_auth { + let root_cert_store = load_trust_anchors(config)?.map_or_else( + || { + Err(zerror!( + "Missing root certificates while client authentication is enabled." + )) + }, + Ok, + )?; + let client_auth = WebPkiClientVerifier::builder(root_cert_store.into()).build()?; + ServerConfig::builder_with_protocol_versions(&[&TLS13]) + .with_client_cert_verifier(client_auth) + .with_single_cert(certs, keys.remove(0)) + .map_err(|e| zerror!(e))? + } else { + ServerConfig::builder() + .with_no_client_auth() + .with_single_cert(certs, keys.remove(0)) + .map_err(|e| zerror!(e))? + }; + Ok(TlsServerConfig { server_config: sc }) + } + + async fn load_tls_private_key(config: &Config<'_>) -> ZResult> { + load_tls_key( + config, + TLS_SERVER_PRIVATE_KEY_RAW, + TLS_SERVER_PRIVATE_KEY_FILE, + TLS_SERVER_PRIVATE_KEY_BASE_64, + ) + .await + } + + async fn load_tls_certificate(config: &Config<'_>) -> ZResult> { + load_tls_certificate( + config, + TLS_SERVER_CERTIFICATE_RAW, + TLS_SERVER_CERTIFICATE_FILE, + TLS_SERVER_CERTIFICATE_BASE64, + ) + .await + } +} + +pub(crate) struct TlsClientConfig { + pub(crate) client_config: ClientConfig, +} + +impl TlsClientConfig { + pub async fn new(config: &Config<'_>) -> ZResult { + let tls_client_server_auth: bool = match config.get(TLS_CLIENT_AUTH) { + Some(s) => s + .parse() + .map_err(|_| zerror!("Unknown client auth argument: {}", s))?, + None => false, + }; + + let tls_server_name_verification: bool = match config.get(TLS_SERVER_NAME_VERIFICATION) { + Some(s) => { + let s: bool = s + .parse() + .map_err(|_| zerror!("Unknown server name verification argument: {}", s))?; + if s { + tracing::warn!("Skipping name verification of servers"); + } + s + } + None => false, + }; + + // Allows mixed user-generated CA and webPKI CA + tracing::debug!("Loading default Web PKI certificates."); + let mut root_cert_store = RootCertStore { + roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(), + }; + + if let Some(custom_root_cert) = load_trust_anchors(config)? { + tracing::debug!("Loading user-generated certificates."); + root_cert_store.extend(custom_root_cert.roots); + } + + let cc = if tls_client_server_auth { + tracing::debug!("Loading client authentication key and certificate..."); + let tls_client_private_key = TlsClientConfig::load_tls_private_key(config).await?; + let tls_client_certificate = TlsClientConfig::load_tls_certificate(config).await?; + + let certs: Vec = + rustls_pemfile::certs(&mut Cursor::new(&tls_client_certificate)) + .collect::>() + .map_err(|err| zerror!("Error processing client certificate: {err}."))?; + + let mut keys: Vec = + rustls_pemfile::rsa_private_keys(&mut Cursor::new(&tls_client_private_key)) + .map(|x| x.map(PrivateKeyDer::from)) + .collect::>() + .map_err(|err| zerror!("Error processing client key: {err}."))?; + + if keys.is_empty() { + keys = + rustls_pemfile::pkcs8_private_keys(&mut Cursor::new(&tls_client_private_key)) + .map(|x| x.map(PrivateKeyDer::from)) + .collect::>() + .map_err(|err| zerror!("Error processing client key: {err}."))?; + } + + if keys.is_empty() { + keys = rustls_pemfile::ec_private_keys(&mut Cursor::new(&tls_client_private_key)) + .map(|x| x.map(PrivateKeyDer::from)) + .collect::>() + .map_err(|err| zerror!("Error processing client key: {err}."))?; + } + + if keys.is_empty() { + bail!("No private key found for TLS client."); + } + + let builder = ClientConfig::builder_with_protocol_versions(&[&TLS13]); + + if tls_server_name_verification { + builder + .with_root_certificates(root_cert_store) + .with_client_auth_cert(certs, keys.remove(0)) + } else { + builder + .dangerous() + .with_custom_certificate_verifier(Arc::new(WebPkiVerifierAnyServerName::new( + root_cert_store, + ))) + .with_client_auth_cert(certs, keys.remove(0)) + } + .map_err(|e| zerror!("Bad certificate/key: {}", e))? + } else { + let builder = ClientConfig::builder(); + if tls_server_name_verification { + builder + .with_root_certificates(root_cert_store) + .with_no_client_auth() + } else { + builder + .dangerous() + .with_custom_certificate_verifier(Arc::new(WebPkiVerifierAnyServerName::new( + root_cert_store, + ))) + .with_no_client_auth() + } + }; + Ok(TlsClientConfig { client_config: cc }) + } + + async fn load_tls_private_key(config: &Config<'_>) -> ZResult> { + load_tls_key( + config, + TLS_CLIENT_PRIVATE_KEY_RAW, + TLS_CLIENT_PRIVATE_KEY_FILE, + TLS_CLIENT_PRIVATE_KEY_BASE64, + ) + .await + } + + async fn load_tls_certificate(config: &Config<'_>) -> ZResult> { + load_tls_certificate( + config, + TLS_CLIENT_CERTIFICATE_RAW, + TLS_CLIENT_CERTIFICATE_FILE, + TLS_CLIENT_CERTIFICATE_BASE64, + ) + .await + } +} + +fn process_pem(pem: &mut dyn io::BufRead) -> ZResult>> { + let certs: Vec = rustls_pemfile::certs(pem) + .map(|result| result.map_err(|err| zerror!("Error processing PEM certificates: {err}."))) + .collect::, ZError>>()?; + + let trust_anchors: Vec = certs + .into_iter() + .map(|cert| { + anchor_from_trusted_cert(&cert) + .map_err(|err| zerror!("Error processing trust anchor: {err}.")) + .map(|trust_anchor| trust_anchor.to_owned()) + }) + .collect::, ZError>>()?; + + Ok(trust_anchors) +} + +async fn load_tls_key( + config: &Config<'_>, + tls_private_key_raw_config_key: &str, + tls_private_key_file_config_key: &str, + tls_private_key_base64_config_key: &str, +) -> ZResult> { + if let Some(value) = config.get(tls_private_key_raw_config_key) { + return Ok(value.as_bytes().to_vec()); + } + + if let Some(b64_key) = config.get(tls_private_key_base64_config_key) { + return base64_decode(b64_key); + } + + if let Some(value) = config.get(tls_private_key_file_config_key) { + return Ok(tokio::fs::read(value) + .await + .map_err(|e| zerror!("Invalid TLS private key file: {}", e))?) + .and_then(|result| { + if result.is_empty() { + Err(zerror!("Empty TLS key.").into()) + } else { + Ok(result) + } + }); + } + Err(zerror!("Missing TLS private key.").into()) +} + +async fn load_tls_certificate( + config: &Config<'_>, + tls_certificate_raw_config_key: &str, + tls_certificate_file_config_key: &str, + tls_certificate_base64_config_key: &str, +) -> ZResult> { + if let Some(value) = config.get(tls_certificate_raw_config_key) { + return Ok(value.as_bytes().to_vec()); + } + + if let Some(b64_certificate) = config.get(tls_certificate_base64_config_key) { + return base64_decode(b64_certificate); + } + + if let Some(value) = config.get(tls_certificate_file_config_key) { + return Ok(tokio::fs::read(value) + .await + .map_err(|e| zerror!("Invalid TLS certificate file: {}", e))?); + } + Err(zerror!("Missing tls certificates.").into()) +} + +fn load_trust_anchors(config: &Config<'_>) -> ZResult> { + let mut root_cert_store = RootCertStore::empty(); + if let Some(value) = config.get(TLS_ROOT_CA_CERTIFICATE_RAW) { + let mut pem = BufReader::new(value.as_bytes()); + let trust_anchors = process_pem(&mut pem)?; + root_cert_store.extend(trust_anchors); + return Ok(Some(root_cert_store)); + } + + if let Some(b64_certificate) = config.get(TLS_ROOT_CA_CERTIFICATE_BASE64) { + let certificate_pem = base64_decode(b64_certificate)?; + let mut pem = BufReader::new(certificate_pem.as_slice()); + let trust_anchors = process_pem(&mut pem)?; + root_cert_store.extend(trust_anchors); + return Ok(Some(root_cert_store)); + } + + if let Some(filename) = config.get(TLS_ROOT_CA_CERTIFICATE_FILE) { + let mut pem = BufReader::new(File::open(filename)?); + let trust_anchors = process_pem(&mut pem)?; + root_cert_store.extend(trust_anchors); + return Ok(Some(root_cert_store)); + } + Ok(None) +} + +pub fn base64_decode(data: &str) -> ZResult> { + use base64::engine::general_purpose; + use base64::Engine; + Ok(general_purpose::STANDARD + .decode(data) + .map_err(|e| zerror!("Unable to perform base64 decoding: {e:?}"))?) +} + +pub async fn get_tls_addr(address: &Address<'_>) -> ZResult { + match tokio::net::lookup_host(address.as_str()).await?.next() { + Some(addr) => Ok(addr), + None => bail!("Couldn't resolve TLS locator address: {}", address), + } +} + +pub fn get_tls_host<'a>(address: &'a Address<'a>) -> ZResult<&'a str> { + address + .as_str() + .split(':') + .next() + .ok_or_else(|| zerror!("Invalid TLS address").into()) +} + +pub fn get_tls_server_name<'a>(address: &'a Address<'a>) -> ZResult> { + Ok(ServerName::try_from(get_tls_host(address)?).map_err(|e| zerror!(e))?) +} diff --git a/io/zenoh-transport/tests/endpoints.rs b/io/zenoh-transport/tests/endpoints.rs index 85e5e4bfef..362644749e 100644 --- a/io/zenoh-transport/tests/endpoints.rs +++ b/io/zenoh-transport/tests/endpoints.rs @@ -255,7 +255,7 @@ async fn endpoint_udp_unix() { #[cfg(feature = "transport_tls")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn endpoint_tls() { - use zenoh_link_commons::tls::config::*; + use zenoh_link::tls::config::*; zenoh_util::try_init_log_from_env(); @@ -334,7 +334,7 @@ AXVFFIgCSluyrolaD6CWD9MqOex4YOfJR2bNxI7lFvuK4AwjyUJzT1U1HXib17mM #[cfg(feature = "transport_quic")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn endpoint_quic() { - use zenoh_link_commons::tls::config::*; + use zenoh_link::tls::config::*; zenoh_util::try_init_log_from_env(); diff --git a/io/zenoh-transport/tests/unicast_authenticator.rs b/io/zenoh-transport/tests/unicast_authenticator.rs index 62b645123b..47e649d698 100644 --- a/io/zenoh-transport/tests/unicast_authenticator.rs +++ b/io/zenoh-transport/tests/unicast_authenticator.rs @@ -719,7 +719,7 @@ async fn authenticator_unix() { #[cfg(feature = "transport_tls")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn authenticator_tls() { - use zenoh_link_commons::tls::config::*; + use zenoh_link::tls::config::*; zenoh_util::try_init_log_from_env(); @@ -819,7 +819,7 @@ R+IdLiXcyIkg0m9N8I17p0ljCSkbrgGMD3bbePRTfg== #[cfg(feature = "transport_quic")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn authenticator_quic() { - use zenoh_link_commons::tls::config::*; + use zenoh_link::tls::config::*; zenoh_util::try_init_log_from_env(); diff --git a/io/zenoh-transport/tests/unicast_multilink.rs b/io/zenoh-transport/tests/unicast_multilink.rs index 6ae96a79b1..05e44a26b0 100644 --- a/io/zenoh-transport/tests/unicast_multilink.rs +++ b/io/zenoh-transport/tests/unicast_multilink.rs @@ -529,7 +529,7 @@ mod tests { #[cfg(feature = "transport_tls")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn multilink_tls_only() { - use zenoh_link_commons::tls::config::*; + use zenoh_link::tls::config::*; zenoh_util::try_init_log_from_env(); @@ -628,7 +628,7 @@ R+IdLiXcyIkg0m9N8I17p0ljCSkbrgGMD3bbePRTfg== #[cfg(feature = "transport_quic")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn multilink_quic_only() { - use zenoh_link_commons::tls::config::*; + use zenoh_link::tls::config::*; // NOTE: this an auto-generated pair of certificate and key. // The target domain is localhost, so it has no real diff --git a/io/zenoh-transport/tests/unicast_openclose.rs b/io/zenoh-transport/tests/unicast_openclose.rs index d44c514d25..7f86337f3d 100644 --- a/io/zenoh-transport/tests/unicast_openclose.rs +++ b/io/zenoh-transport/tests/unicast_openclose.rs @@ -559,7 +559,7 @@ async fn openclose_unix_only() { #[cfg(feature = "transport_tls")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn openclose_tls_only() { - use zenoh_link_commons::tls::config::*; + use zenoh_link::tls::config::*; zenoh_util::try_init_log_from_env(); // NOTE: this an auto-generated pair of certificate and key. @@ -657,7 +657,7 @@ R+IdLiXcyIkg0m9N8I17p0ljCSkbrgGMD3bbePRTfg== #[cfg(feature = "transport_quic")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn openclose_quic_only() { - use zenoh_link_commons::tls::config::*; + use zenoh_link::tls::config::*; // NOTE: this an auto-generated pair of certificate and key. // The target domain is localhost, so it has no real diff --git a/io/zenoh-transport/tests/unicast_transport.rs b/io/zenoh-transport/tests/unicast_transport.rs index 0c8ac25c74..39252b9548 100644 --- a/io/zenoh-transport/tests/unicast_transport.rs +++ b/io/zenoh-transport/tests/unicast_transport.rs @@ -991,7 +991,7 @@ async fn transport_unicast_tcp_udp_unix() { #[cfg(all(feature = "transport_tls", target_family = "unix"))] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn transport_unicast_tls_only_server() { - use zenoh_link_commons::tls::config::*; + use zenoh_link::tls::config::*; zenoh_util::try_init_log_from_env(); @@ -1037,7 +1037,7 @@ async fn transport_unicast_tls_only_server() { #[cfg(feature = "transport_quic")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn transport_unicast_quic_only_server() { - use zenoh_link_commons::tls::config::*; + use zenoh_link::tls::config::*; zenoh_util::try_init_log_from_env(); // Define the locator @@ -1082,7 +1082,7 @@ async fn transport_unicast_quic_only_server() { #[cfg(all(feature = "transport_tls", target_family = "unix"))] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn transport_unicast_tls_only_mutual_success() { - use zenoh_link_commons::tls::config::*; + use zenoh_link::tls::config::*; zenoh_util::try_init_log_from_env(); @@ -1154,7 +1154,7 @@ async fn transport_unicast_tls_only_mutual_success() { #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn transport_unicast_tls_only_mutual_no_client_certs_failure() { use std::vec; - use zenoh_link_commons::tls::config::*; + use zenoh_link::tls::config::*; zenoh_util::try_init_log_from_env(); @@ -1222,7 +1222,7 @@ async fn transport_unicast_tls_only_mutual_no_client_certs_failure() { #[cfg(all(feature = "transport_tls", target_family = "unix"))] #[test] fn transport_unicast_tls_only_mutual_wrong_client_certs_failure() { - use zenoh_link_commons::tls::config::*; + use zenoh_link::tls::config::*; zenoh_util::try_init_log_from_env(); From 8c0c619eed788a4ada80c80308ab7e6e2148783d Mon Sep 17 00:00:00 2001 From: gabrik Date: Wed, 24 Apr 2024 11:39:25 +0200 Subject: [PATCH 4/6] test(quic-mlts): added tests for QUIC with mTLS, using rustls 0.22 to workaround the default CryptoProvider panic Signed-off-by: gabrik --- Cargo.lock | 166 +----------- Cargo.toml | 4 +- io/zenoh-transport/tests/unicast_transport.rs | 236 +++++++++++++++++- 3 files changed, 242 insertions(+), 164 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e2b5be916b..36078d0238 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -451,33 +451,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "aws-lc-rs" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5509d663b2c00ee421bda8d6a24d6c42e15970957de1701b8df9f6fbe5707df1" -dependencies = [ - "aws-lc-sys", - "mirai-annotations", - "paste", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d5d317212c2a78d86ba6622e969413c38847b62f48111f8b763af3dac2f9840" -dependencies = [ - "bindgen", - "cc", - "cmake", - "dunce", - "fs_extra", - "libc", - "paste", -] - [[package]] name = "backtrace" version = "0.3.69" @@ -532,29 +505,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bindgen" -version = "0.69.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" -dependencies = [ - "bitflags 2.4.2", - "cexpr", - "clang-sys", - "itertools", - "lazy_static", - "lazycell", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.33", - "which", -] - [[package]] name = "bit-set" version = "0.5.3" @@ -675,19 +625,9 @@ version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ - "jobserver", "libc", ] -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" version = "0.1.10" @@ -761,17 +701,6 @@ dependencies = [ "inout", ] -[[package]] -name = "clang-sys" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" version = "4.4.11" @@ -812,15 +741,6 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" -[[package]] -name = "cmake" -version = "0.1.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" -dependencies = [ - "cc", -] - [[package]] name = "cobs" version = "0.2.3" @@ -1162,12 +1082,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" -[[package]] -name = "dunce" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" - [[package]] name = "dyn-clone" version = "1.0.13" @@ -1361,12 +1275,6 @@ dependencies = [ "num", ] -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - [[package]] name = "futures" version = "0.3.28" @@ -1543,12 +1451,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - [[package]] name = "gloo-timers" version = "0.2.6" @@ -1915,15 +1817,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" -[[package]] -name = "jobserver" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" -dependencies = [ - "libc", -] - [[package]] name = "js-sys" version = "0.3.64" @@ -2008,12 +1901,6 @@ dependencies = [ "spin 0.5.2", ] -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" version = "0.2.153" @@ -2193,12 +2080,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "mirai-annotations" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" - [[package]] name = "nanorand" version = "0.7.0" @@ -2765,16 +2646,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "prettyplease" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" -dependencies = [ - "proc-macro2", - "syn 2.0.33", -] - [[package]] name = "proc-macro-hack" version = "0.5.20+deprecated" @@ -3247,13 +3118,11 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.5" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afabcee0551bd1aa3e18e5adbf2c0544722014b899adb31bd186ec638d3da97e" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ - "aws-lc-rs", "log", - "once_cell", "ring 0.17.6", "rustls-pki-types", "rustls-webpki 0.102.2", @@ -3327,7 +3196,6 @@ version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ - "aws-lc-rs", "ring 0.17.6", "rustls-pki-types", "untrusted 0.9.0", @@ -3646,12 +3514,6 @@ dependencies = [ "dirs", ] -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - [[package]] name = "signal-hook" version = "0.3.17" @@ -4175,11 +4037,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ - "rustls 0.23.5", + "rustls 0.22.4", "rustls-pki-types", "tokio", ] @@ -4757,18 +4619,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix 0.38.32", -] - [[package]] name = "win-sys" version = "0.3.1" @@ -5262,7 +5112,7 @@ dependencies = [ "base64 0.21.4", "flume", "futures", - "rustls 0.23.5", + "rustls 0.22.4", "rustls-webpki 0.102.2", "serde", "tokio", @@ -5353,13 +5203,13 @@ dependencies = [ "async-trait", "base64 0.21.4", "futures", - "rustls 0.23.5", + "rustls 0.22.4", "rustls-pemfile 2.0.0", "rustls-pki-types", "rustls-webpki 0.102.2", "secrecy", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.25.0", "tokio-util", "tracing", "webpki-roots", diff --git a/Cargo.toml b/Cargo.toml index 115f11c119..a084341c18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -132,7 +132,7 @@ ron = "0.8.1" ringbuffer-spsc = "0.1.9" rsa = "0.9" rustc_version = "0.4.0" -rustls = {version = "0.23", features = ["ring"]} +rustls = {version = "0.22.2"} rustls-native-certs = "0.7.0" rustls-pemfile = "2.0.0" rustls-webpki = "0.102.0" @@ -155,7 +155,7 @@ token-cell = { version = "1.4.2", default-features = false } tokio = { version = "1.35.1", default-features = false } # Default features are disabled due to some crates' requirements tokio-util = "0.7.10" tokio-tungstenite = "0.21" -tokio-rustls = "0.26.0" +tokio-rustls = "0.25.0" # tokio-vsock = see: io/zenoh-links/zenoh-link-vsock/Cargo.toml (workspaces does not support platform dependent dependencies) console-subscriber = "0.2" typenum = "1.16.0" diff --git a/io/zenoh-transport/tests/unicast_transport.rs b/io/zenoh-transport/tests/unicast_transport.rs index 39252b9548..33cfbceb17 100644 --- a/io/zenoh-transport/tests/unicast_transport.rs +++ b/io/zenoh-transport/tests/unicast_transport.rs @@ -69,7 +69,10 @@ use zenoh_transport::{ // the key and certificate brought in by the client. Similarly the server's certificate authority // will validate the key and certificate brought in by the server in front of the client. // -#[cfg(all(feature = "transport_tls", target_family = "unix"))] +#[cfg(all( + any(feature = "transport_tls", feature = "transport_quic"), + target_family = "unix" +))] const CLIENT_KEY: &str = "-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAsfqAuhElN4HnyeqLovSd4Qe+nNv5AwCjSO+HFiF30x3vQ1Hi qRA0UmyFlSqBnFH3TUHm4Jcad40QfrX8f11NKGZdpvKHsMYqYjZnYkRFGS2s4fQy @@ -98,7 +101,10 @@ tYsqC2FtWzY51VOEKNpnfH7zH5n+bjoI9nAEAW63TK9ZKkr2hRGsDhJdGzmLfQ7v F6/CuIw9EsAq6qIB8O88FXQqald+BZOx6AzB8Oedsz/WtMmIEmr/+Q== -----END RSA PRIVATE KEY-----"; -#[cfg(all(feature = "transport_tls", target_family = "unix"))] +#[cfg(all( + any(feature = "transport_tls", feature = "transport_quic"), + target_family = "unix" +))] const CLIENT_CERT: &str = "-----BEGIN CERTIFICATE----- MIIDLjCCAhagAwIBAgIIeUtmIdFQznMwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE AxMVbWluaWNhIHJvb3QgY2EgMDc4ZGE3MCAXDTIzMDMwNjE2MDMxOFoYDzIxMjMw @@ -120,7 +126,10 @@ p5e60QweRuJsb60aUaCG8HoICevXYK2fFqCQdlb5sIqQqXyN2K6HuKAFywsjsGyJ abY= -----END CERTIFICATE-----"; -#[cfg(all(feature = "transport_tls", target_family = "unix"))] +#[cfg(all( + any(feature = "transport_tls", feature = "transport_quic"), + target_family = "unix" +))] const CLIENT_CA: &str = "-----BEGIN CERTIFICATE----- MIIDSzCCAjOgAwIBAgIIB42n1ZIkOakwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE AxMVbWluaWNhIHJvb3QgY2EgMDc4ZGE3MCAXDTIzMDMwNjE2MDMwN1oYDzIxMjMw @@ -1037,7 +1046,7 @@ async fn transport_unicast_tls_only_server() { #[cfg(feature = "transport_quic")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn transport_unicast_quic_only_server() { - use zenoh_link::tls::config::*; + use zenoh_link::quic::config::*; zenoh_util::try_init_log_from_env(); // Define the locator @@ -1298,6 +1307,225 @@ fn transport_unicast_tls_only_mutual_wrong_client_certs_failure() { assert!(result.is_err()); } +#[cfg(all(feature = "transport_quic", target_family = "unix"))] +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn transport_unicast_quic_only_mutual_success() { + use zenoh_link::quic::config::*; + + zenoh_util::try_init_log_from_env(); + + let client_auth = "true"; + + // Define the locator + let mut client_endpoint: EndPoint = ("quic/localhost:10461").parse().unwrap(); + client_endpoint + .config_mut() + .extend( + [ + (TLS_ROOT_CA_CERTIFICATE_RAW, SERVER_CA), + (TLS_CLIENT_CERTIFICATE_RAW, CLIENT_CERT), + (TLS_CLIENT_PRIVATE_KEY_RAW, CLIENT_KEY), + (TLS_CLIENT_AUTH, client_auth), + ] + .iter() + .map(|(k, v)| ((*k).to_owned(), (*v).to_owned())), + ) + .unwrap(); + + // Define the locator + let mut server_endpoint: EndPoint = ("quic/localhost:10461").parse().unwrap(); + server_endpoint + .config_mut() + .extend( + [ + (TLS_ROOT_CA_CERTIFICATE_RAW, CLIENT_CA), + (TLS_SERVER_CERTIFICATE_RAW, SERVER_CERT), + (TLS_SERVER_PRIVATE_KEY_RAW, SERVER_KEY), + (TLS_CLIENT_AUTH, client_auth), + ] + .iter() + .map(|(k, v)| ((*k).to_owned(), (*v).to_owned())), + ) + .unwrap(); + // Define the reliability and congestion control + let channel = [ + Channel { + priority: Priority::default(), + reliability: Reliability::Reliable, + }, + Channel { + priority: Priority::default(), + reliability: Reliability::BestEffort, + }, + Channel { + priority: Priority::RealTime, + reliability: Reliability::Reliable, + }, + Channel { + priority: Priority::RealTime, + reliability: Reliability::BestEffort, + }, + ]; + // Run + let client_endpoints = vec![client_endpoint]; + let server_endpoints = vec![server_endpoint]; + run_with_universal_transport( + &client_endpoints, + &server_endpoints, + &channel, + &MSG_SIZE_ALL, + ) + .await; +} + +#[cfg(all(feature = "transport_quic", target_family = "unix"))] +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn transport_unicast_quic_only_mutual_no_client_certs_failure() { + use std::vec; + use zenoh_link::quic::config::*; + + zenoh_util::try_init_log_from_env(); + + // Define the locator + let mut client_endpoint: EndPoint = ("quic/localhost:10462").parse().unwrap(); + client_endpoint + .config_mut() + .extend( + [(TLS_ROOT_CA_CERTIFICATE_RAW, SERVER_CA)] + .iter() + .map(|(k, v)| ((*k).to_owned(), (*v).to_owned())), + ) + .unwrap(); + + // Define the locator + let mut server_endpoint: EndPoint = ("quic/localhost:10462").parse().unwrap(); + server_endpoint + .config_mut() + .extend( + [ + (TLS_ROOT_CA_CERTIFICATE_RAW, CLIENT_CA), + (TLS_SERVER_CERTIFICATE_RAW, SERVER_CERT), + (TLS_SERVER_PRIVATE_KEY_RAW, SERVER_KEY), + (TLS_CLIENT_AUTH, "true"), + ] + .iter() + .map(|(k, v)| ((*k).to_owned(), (*v).to_owned())), + ) + .unwrap(); + // Define the reliability and congestion control + let channel = [ + Channel { + priority: Priority::default(), + reliability: Reliability::Reliable, + }, + Channel { + priority: Priority::default(), + reliability: Reliability::BestEffort, + }, + Channel { + priority: Priority::RealTime, + reliability: Reliability::Reliable, + }, + Channel { + priority: Priority::RealTime, + reliability: Reliability::BestEffort, + }, + ]; + // Run + let client_endpoints = vec![client_endpoint]; + let server_endpoints = vec![server_endpoint]; + let result = std::panic::catch_unwind(|| { + tokio::runtime::Runtime::new() + .unwrap() + .block_on(run_with_universal_transport( + &client_endpoints, + &server_endpoints, + &channel, + &MSG_SIZE_ALL, + )) + }); + assert!(result.is_err()); +} + +#[cfg(all(feature = "transport_quic", target_family = "unix"))] +#[test] +fn transport_unicast_quic_only_mutual_wrong_client_certs_failure() { + use zenoh_link::quic::config::*; + + zenoh_util::try_init_log_from_env(); + + let client_auth = "true"; + + // Define the locator + let mut client_endpoint: EndPoint = ("quic/localhost:10463").parse().unwrap(); + client_endpoint + .config_mut() + .extend( + [ + (TLS_ROOT_CA_CERTIFICATE_RAW, SERVER_CA), + // Using the SERVER_CERT and SERVER_KEY in the client to simulate the case the client has + // wrong certificates and keys. The SERVER_CA (cetificate authority) will not recognize + // these certificates as it is expecting to receive CLIENT_CERT and CLIENT_KEY from the + // client. + (TLS_CLIENT_CERTIFICATE_RAW, SERVER_CERT), + (TLS_CLIENT_PRIVATE_KEY_RAW, SERVER_KEY), + (TLS_CLIENT_AUTH, client_auth), + ] + .iter() + .map(|(k, v)| ((*k).to_owned(), (*v).to_owned())), + ) + .unwrap(); + + // Define the locator + let mut server_endpoint: EndPoint = ("quic/localhost:10463").parse().unwrap(); + server_endpoint + .config_mut() + .extend( + [ + (TLS_ROOT_CA_CERTIFICATE_RAW, CLIENT_CA), + (TLS_SERVER_CERTIFICATE_RAW, SERVER_CERT), + (TLS_SERVER_PRIVATE_KEY_RAW, SERVER_KEY), + (TLS_CLIENT_AUTH, client_auth), + ] + .iter() + .map(|(k, v)| ((*k).to_owned(), (*v).to_owned())), + ) + .unwrap(); + // Define the reliability and congestion control + let channel = [ + Channel { + priority: Priority::default(), + reliability: Reliability::Reliable, + }, + Channel { + priority: Priority::default(), + reliability: Reliability::BestEffort, + }, + Channel { + priority: Priority::RealTime, + reliability: Reliability::Reliable, + }, + Channel { + priority: Priority::RealTime, + reliability: Reliability::BestEffort, + }, + ]; + // Run + let client_endpoints = vec![client_endpoint]; + let server_endpoints = vec![server_endpoint]; + let result = std::panic::catch_unwind(|| { + tokio::runtime::Runtime::new() + .unwrap() + .block_on(run_with_universal_transport( + &client_endpoints, + &server_endpoints, + &channel, + &MSG_SIZE_ALL, + )) + }); + assert!(result.is_err()); +} + #[test] fn transport_unicast_qos_and_lowlatency_failure() { struct TestPeer; From 68b5b1878ed1e614ae67dada2376d285c7fae248 Mon Sep 17 00:00:00 2001 From: gabrik Date: Wed, 24 Apr 2024 15:40:58 +0200 Subject: [PATCH 5/6] chore: addressing comments Signed-off-by: gabrik --- Cargo.toml | 4 ++-- io/zenoh-links/zenoh-link-quic/Cargo.toml | 4 ++-- io/zenoh-links/zenoh-link-quic/src/unicast.rs | 4 ++-- io/zenoh-transport/tests/endpoints.rs | 2 +- io/zenoh-transport/tests/unicast_authenticator.rs | 2 +- io/zenoh-transport/tests/unicast_multilink.rs | 2 +- io/zenoh-transport/tests/unicast_openclose.rs | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a084341c18..dc24991488 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -122,7 +122,7 @@ petgraph = "0.6.3" pnet = "0.34" pnet_datalink = "0.34" proc-macro2 = "1.0.51" -quinn = {version = "0.10.1"} +quinn = "0.10.1" quote = "1.0.23" rand = { version = "0.8.5", default-features = false } # Default features are disabled due to usage in no_std crates rand_chacha = "0.3.1" @@ -132,7 +132,7 @@ ron = "0.8.1" ringbuffer-spsc = "0.1.9" rsa = "0.9" rustc_version = "0.4.0" -rustls = {version = "0.22.2"} +rustls = "0.22.2" rustls-native-certs = "0.7.0" rustls-pemfile = "2.0.0" rustls-webpki = "0.102.0" diff --git a/io/zenoh-links/zenoh-link-quic/Cargo.toml b/io/zenoh-links/zenoh-link-quic/Cargo.toml index 47fbd35cb7..0e1c720d78 100644 --- a/io/zenoh-links/zenoh-link-quic/Cargo.toml +++ b/io/zenoh-links/zenoh-link-quic/Cargo.toml @@ -30,8 +30,7 @@ base64 = { workspace = true } futures = { workspace = true } quinn = { workspace = true } rustls-native-certs = { workspace = true } -rustls-pemfile = { version = "1" } -rustls-pki-types = { version = "1.0" } +rustls-pki-types = { workspace = true } rustls-webpki = { workspace = true } secrecy = { workspace = true } tokio = { workspace = true, features = [ @@ -55,3 +54,4 @@ zenoh-util = { workspace = true } # Lock due to quinn not supporting rustls 0.22 yet rustls = { version = "0.21", features = ["dangerous_configuration", "quic"] } tokio-rustls = "0.24.1" +rustls-pemfile = { version = "1" } diff --git a/io/zenoh-links/zenoh-link-quic/src/unicast.rs b/io/zenoh-links/zenoh-link-quic/src/unicast.rs index ec9e54a0bf..f97ed1eede 100644 --- a/io/zenoh-links/zenoh-link-quic/src/unicast.rs +++ b/io/zenoh-links/zenoh-link-quic/src/unicast.rs @@ -219,7 +219,7 @@ impl LinkManagerUnicastTrait for LinkManagerUnicastQuic { // Initialize the QUIC connection let mut client_crypto = TlsClientConfig::new(&epconf) .await - .map_err(|e| zerror!("Cannot create a new QUIC client on {addr}. {e}"))?; + .map_err(|e| zerror!("Cannot create a new QUIC client on {addr}: {e}"))?; client_crypto.client_config.alpn_protocols = ALPN_QUIC_HTTP.iter().map(|&x| x.into()).collect(); @@ -274,7 +274,7 @@ impl LinkManagerUnicastTrait for LinkManagerUnicastQuic { // Server config let mut server_crypto = TlsServerConfig::new(&epconf) .await - .map_err(|e| zerror!("Cannot create a new QUIC listener on {addr}. {e}"))?; + .map_err(|e| zerror!("Cannot create a new QUIC listener on {addr}: {e}"))?; server_crypto.server_config.alpn_protocols = ALPN_QUIC_HTTP.iter().map(|&x| x.into()).collect(); let mut server_config = diff --git a/io/zenoh-transport/tests/endpoints.rs b/io/zenoh-transport/tests/endpoints.rs index 362644749e..6269f78cb9 100644 --- a/io/zenoh-transport/tests/endpoints.rs +++ b/io/zenoh-transport/tests/endpoints.rs @@ -334,7 +334,7 @@ AXVFFIgCSluyrolaD6CWD9MqOex4YOfJR2bNxI7lFvuK4AwjyUJzT1U1HXib17mM #[cfg(feature = "transport_quic")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn endpoint_quic() { - use zenoh_link::tls::config::*; + use zenoh_link::quic::config::*; zenoh_util::try_init_log_from_env(); diff --git a/io/zenoh-transport/tests/unicast_authenticator.rs b/io/zenoh-transport/tests/unicast_authenticator.rs index 47e649d698..a232584cff 100644 --- a/io/zenoh-transport/tests/unicast_authenticator.rs +++ b/io/zenoh-transport/tests/unicast_authenticator.rs @@ -819,7 +819,7 @@ R+IdLiXcyIkg0m9N8I17p0ljCSkbrgGMD3bbePRTfg== #[cfg(feature = "transport_quic")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn authenticator_quic() { - use zenoh_link::tls::config::*; + use zenoh_link::quic::config::*; zenoh_util::try_init_log_from_env(); diff --git a/io/zenoh-transport/tests/unicast_multilink.rs b/io/zenoh-transport/tests/unicast_multilink.rs index 05e44a26b0..d69a30ac9d 100644 --- a/io/zenoh-transport/tests/unicast_multilink.rs +++ b/io/zenoh-transport/tests/unicast_multilink.rs @@ -628,7 +628,7 @@ R+IdLiXcyIkg0m9N8I17p0ljCSkbrgGMD3bbePRTfg== #[cfg(feature = "transport_quic")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn multilink_quic_only() { - use zenoh_link::tls::config::*; + use zenoh_link::quic::config::*; // NOTE: this an auto-generated pair of certificate and key. // The target domain is localhost, so it has no real diff --git a/io/zenoh-transport/tests/unicast_openclose.rs b/io/zenoh-transport/tests/unicast_openclose.rs index 7f86337f3d..a671de14a8 100644 --- a/io/zenoh-transport/tests/unicast_openclose.rs +++ b/io/zenoh-transport/tests/unicast_openclose.rs @@ -657,7 +657,7 @@ R+IdLiXcyIkg0m9N8I17p0ljCSkbrgGMD3bbePRTfg== #[cfg(feature = "transport_quic")] #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn openclose_quic_only() { - use zenoh_link::tls::config::*; + use zenoh_link::quic::config::*; // NOTE: this an auto-generated pair of certificate and key. // The target domain is localhost, so it has no real From d37ed2c45e26917c822481021582fcf36bdfa103 Mon Sep 17 00:00:00 2001 From: Luca Cominardi Date: Wed, 24 Apr 2024 15:47:32 +0200 Subject: [PATCH 6/6] Apply suggestions from code review --- io/zenoh-link-commons/src/lib.rs | 1 - io/zenoh-links/zenoh-link-quic/src/lib.rs | 2 +- io/zenoh-links/zenoh-link-quic/src/unicast.rs | 1 - io/zenoh-links/zenoh-link-quic/src/utils.rs | 4 ++-- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/io/zenoh-link-commons/src/lib.rs b/io/zenoh-link-commons/src/lib.rs index 1bb081ded1..f9ad7166ee 100644 --- a/io/zenoh-link-commons/src/lib.rs +++ b/io/zenoh-link-commons/src/lib.rs @@ -22,7 +22,6 @@ extern crate alloc; mod listener; mod multicast; pub mod tls; - mod unicast; use alloc::{borrow::ToOwned, boxed::Box, string::String, vec, vec::Vec}; diff --git a/io/zenoh-links/zenoh-link-quic/src/lib.rs b/io/zenoh-links/zenoh-link-quic/src/lib.rs index 4b57ea88ca..0c9bc7365e 100644 --- a/io/zenoh-links/zenoh-link-quic/src/lib.rs +++ b/io/zenoh-links/zenoh-link-quic/src/lib.rs @@ -76,7 +76,7 @@ pub mod config { pub const TLS_SERVER_PRIVATE_KEY_FILE: &str = "server_private_key_file"; pub const TLS_SERVER_PRIVATE_KEY_RAW: &str = "server_private_key_raw"; - pub const TLS_SERVER_PRIVATE_KEY_BASE_64: &str = "server_private_key_base64"; + pub const TLS_SERVER_PRIVATE_KEY_BASE64: &str = "server_private_key_base64"; pub const TLS_SERVER_CERTIFICATE_FILE: &str = "server_certificate_file"; pub const TLS_SERVER_CERTIFICATE_RAW: &str = "server_certificate_raw"; diff --git a/io/zenoh-links/zenoh-link-quic/src/unicast.rs b/io/zenoh-links/zenoh-link-quic/src/unicast.rs index f97ed1eede..452fd8a122 100644 --- a/io/zenoh-links/zenoh-link-quic/src/unicast.rs +++ b/io/zenoh-links/zenoh-link-quic/src/unicast.rs @@ -26,7 +26,6 @@ use std::time::Duration; use tokio::sync::Mutex as AsyncMutex; use tokio_util::sync::CancellationToken; use zenoh_core::zasynclock; - use zenoh_link_commons::{ get_ip_interface_names, LinkManagerUnicastTrait, LinkUnicast, LinkUnicastTrait, ListenersUnicastIP, NewLinkChannelSender, diff --git a/io/zenoh-links/zenoh-link-quic/src/utils.rs b/io/zenoh-links/zenoh-link-quic/src/utils.rs index f7c06dc193..40367599cb 100644 --- a/io/zenoh-links/zenoh-link-quic/src/utils.rs +++ b/io/zenoh-links/zenoh-link-quic/src/utils.rs @@ -69,7 +69,7 @@ impl ConfigurationInspector for TlsConfigurator { } (None, Some(server_private_key)) => { ps.push(( - TLS_SERVER_PRIVATE_KEY_BASE_64, + TLS_SERVER_PRIVATE_KEY_BASE64, server_private_key.expose_secret(), )); } @@ -226,7 +226,7 @@ impl TlsServerConfig { config, TLS_SERVER_PRIVATE_KEY_RAW, TLS_SERVER_PRIVATE_KEY_FILE, - TLS_SERVER_PRIVATE_KEY_BASE_64, + TLS_SERVER_PRIVATE_KEY_BASE64, ) .await }