diff --git a/Cargo.lock b/Cargo.lock index a0b25c49d7..3a874ddcc7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2711,7 +2711,7 @@ dependencies = [ "ring 0.16.20", "rustc-hash", "rustls", - "rustls-native-certs", + "rustls-native-certs 0.6.3", "slab", "thiserror", "tinyvec", @@ -3085,6 +3085,19 @@ dependencies = [ "security-framework", ] +[[package]] +name = "rustls-native-certs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.0.0", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "1.0.3" @@ -4753,7 +4766,7 @@ dependencies = [ "log", "quinn", "rustls", - "rustls-native-certs", + "rustls-native-certs 0.7.0", "rustls-pemfile 2.0.0", "rustls-webpki 0.102.0", "secrecy", diff --git a/Cargo.toml b/Cargo.toml index 4b98269286..bc17a93a6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -125,8 +125,9 @@ ringbuffer-spsc = "0.1.9" rsa = "0.9" rustc_version = "0.4.0" rustls = { version = "0.21.5", features = ["dangerous_configuration"] } -rustls-native-certs = "0.6.2" +rustls-native-certs = "0.7.0" rustls-pemfile = "2.0.0" +rustls-webpki = "0.102.0" schemars = "0.8.12" secrecy = {version = "0.8.0", features = ["serde", "alloc"]} serde = { version = "1.0.154", default-features = false, features = [ @@ -154,7 +155,6 @@ uuid = { version = "1.3.0", default-features = false, features = [ ] } # Default features are disabled due to usage in no_std crates validated_struct = "2.1.0" vec_map = "0.8.2" -rustls-webpki = "0.102.0" webpki-roots = "0.26.0" winapi = { version = "0.3.9", features = ["iphlpapi"] } z-serial = "0.2.1" diff --git a/io/zenoh-links/zenoh-link-quic/src/unicast.rs b/io/zenoh-links/zenoh-link-quic/src/unicast.rs index 70bd3ee769..2b1c59ad23 100644 --- a/io/zenoh-links/zenoh-link-quic/src/unicast.rs +++ b/io/zenoh-links/zenoh-link-quic/src/unicast.rs @@ -23,6 +23,8 @@ use async_std::sync::Mutex as AsyncMutex; use async_std::task; use async_std::task::JoinHandle; use async_trait::async_trait; +use rustls::{Certificate, PrivateKey}; +use rustls_pemfile::Item; use std::collections::HashMap; use std::fmt; use std::io::BufReader; @@ -35,7 +37,7 @@ use zenoh_link_commons::{ LinkManagerUnicastTrait, LinkUnicast, LinkUnicastTrait, NewLinkChannelSender, }; use zenoh_protocol::core::{EndPoint, Locator}; -use zenoh_result::{bail, zerror, ZResult}; +use zenoh_result::{bail, zerror, ZError, ZResult}; use zenoh_sync::Signal; pub struct LinkUnicastQuic { @@ -261,14 +263,16 @@ impl LinkManagerUnicastTrait for LinkManagerUnicastQuic { rustls_native_certs::load_native_certs() .map_err(|e| zerror!("Invalid QUIC CA certificate file: {}", e))? .drain(..) - .map(|x| rustls::Certificate(x.0)) + .map(|x| rustls::Certificate(x.to_vec())) .collect::>() } else { rustls_pemfile::certs(&mut BufReader::new(f.as_slice())) - .map_err(|e| zerror!("Invalid QUIC CA certificate file: {}", e))? - .drain(..) - .map(rustls::Certificate) - .collect::>() + .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))?; @@ -347,10 +351,12 @@ impl LinkManagerUnicastTrait for LinkManagerUnicastQuic { bail!("No QUIC CA certificate has been provided."); }; let certificates = rustls_pemfile::certs(&mut BufReader::new(f.as_slice())) - .map_err(|e| zerror!("Invalid QUIC CA certificate file: {}", e))? - .drain(..) - .map(rustls::Certificate) - .collect(); + .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) { @@ -364,20 +370,24 @@ impl LinkManagerUnicastTrait for LinkManagerUnicastQuic { } else { bail!("No QUIC CA private key has been provided."); }; - let private_key = rustls::PrivateKey( - rustls_pemfile::read_all(&mut BufReader::new(f.as_slice())) - .map_err(|e| zerror!("Invalid QUIC CA private key file: {}", e))? - .iter() - .filter_map(|x| match x { - rustls_pemfile::Item::RSAKey(k) - | rustls_pemfile::Item::PKCS8Key(k) - | rustls_pemfile::Item::ECKey(k) => Some(k.to_vec()), - _ => None, - }) - .take(1) - .next() - .ok_or_else(|| zerror!("No QUIC CA private key has been provided."))?, - ); + let items: Vec = rustls_pemfile::read_all(&mut BufReader::new(f.as_slice())) + .map(|result| { + result.map_err(|err| zerror!("Invalid QUIC CA private key file: {}", err)) + }) + .collect::, ZError>>()?; + + 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() diff --git a/io/zenoh-links/zenoh-link-tls/src/unicast.rs b/io/zenoh-links/zenoh-link-tls/src/unicast.rs index 63c6d63b1e..7761195e4b 100644 --- a/io/zenoh-links/zenoh-link-tls/src/unicast.rs +++ b/io/zenoh-links/zenoh-link-tls/src/unicast.rs @@ -32,7 +32,6 @@ use async_std::task::JoinHandle; use async_trait::async_trait; use futures::io::AsyncReadExt; use futures::io::AsyncWriteExt; -use std::cell::UnsafeCell; use std::collections::HashMap; use std::convert::TryInto; use std::fmt; @@ -42,14 +41,18 @@ use std::net::{IpAddr, Shutdown}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, RwLock}; use std::time::Duration; -use webpki::TrustAnchor; +use std::{cell::UnsafeCell, io}; +use webpki::{ + anchor_from_trusted_cert, + types::{CertificateDer, TrustAnchor}, +}; use zenoh_core::{zasynclock, zread, zwrite}; use zenoh_link_commons::{ LinkManagerUnicastTrait, LinkUnicast, LinkUnicastTrait, NewLinkChannelSender, }; use zenoh_protocol::core::endpoint::Config; use zenoh_protocol::core::{EndPoint, Locator}; -use zenoh_result::{bail, zerror, ZResult}; +use zenoh_result::{bail, zerror, ZError, ZResult}; use zenoh_sync::Signal; pub struct LinkUnicastTls { @@ -525,32 +528,48 @@ impl TlsServerConfig { 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(|result| { + result + .map_err(|err| zerror!("Error processing server certificate: {err}.")) + .map(|der| Certificate(der.to_vec())) + }) + .collect::, ZError>>()?; + let mut keys: Vec = rustls_pemfile::rsa_private_keys(&mut Cursor::new(&tls_server_private_key)) - .map_err(|e| zerror!(e)) - .map(|mut keys| keys.drain(..).map(PrivateKey).collect())?; + .map(|result| { + result + .map_err(|err| zerror!("Error processing server key: {err}.")) + .map(|key| PrivateKey(key.secret_pkcs1_der().to_vec())) + }) + .collect::, ZError>>()?; if keys.is_empty() { keys = rustls_pemfile::pkcs8_private_keys(&mut Cursor::new(&tls_server_private_key)) - .map_err(|e| zerror!(e)) - .map(|mut keys| keys.drain(..).map(PrivateKey).collect())?; + .map(|result| { + result + .map_err(|err| zerror!("Error processing server key: {err}.")) + .map(|key| PrivateKey(key.secret_pkcs8_der().to_vec())) + }) + .collect::, ZError>>()?; } if keys.is_empty() { keys = rustls_pemfile::ec_private_keys(&mut Cursor::new(&tls_server_private_key)) - .map_err(|e| zerror!(e)) - .map(|mut keys| keys.drain(..).map(PrivateKey).collect())?; + .map(|result| { + result + .map_err(|err| zerror!("Error processing server key: {err}.")) + .map(|key| PrivateKey(key.secret_sec1_der().to_vec())) + }) + .collect::, ZError>>()?; } if keys.is_empty() { - bail!("No private key found"); + bail!("No private key found for TLS server."); } - let certs: Vec = - rustls_pemfile::certs(&mut Cursor::new(&tls_server_certificate)) - .map_err(|e| zerror!(e)) - .map(|mut certs| certs.drain(..).map(Certificate).collect())?; - let sc = if tls_server_client_auth { let root_cert_store = load_trust_anchors(config)?.map_or_else( || { @@ -643,23 +662,45 @@ impl TlsClientConfig { let certs: Vec = rustls_pemfile::certs(&mut Cursor::new(&tls_client_certificate)) - .map_err(|e| zerror!(e)) - .map(|mut certs| certs.drain(..).map(Certificate).collect())?; + .map(|result| { + result + .map_err(|err| zerror!("Error processing client certificate: {err}.")) + .map(|der| Certificate(der.to_vec())) + }) + .collect::, ZError>>()?; let mut keys: Vec = rustls_pemfile::rsa_private_keys(&mut Cursor::new(&tls_client_private_key)) - .map_err(|e| zerror!(e)) - .map(|mut keys| keys.drain(..).map(PrivateKey).collect())?; + .map(|result| { + result + .map_err(|err| zerror!("Error processing client key: {err}.")) + .map(|key| PrivateKey(key.secret_pkcs1_der().to_vec())) + }) + .collect::, ZError>>()?; if keys.is_empty() { keys = rustls_pemfile::pkcs8_private_keys(&mut Cursor::new(&tls_client_private_key)) - .map_err(|e| zerror!(e)) - .map(|mut keys| keys.drain(..).map(PrivateKey).collect())?; + .map(|result| { + result + .map_err(|err| zerror!("Error processing client key: {err}.")) + .map(|key| PrivateKey(key.secret_pkcs8_der().to_vec())) + }) + .collect::, ZError>>()?; } if keys.is_empty() { - bail!("No private key found"); + keys = rustls_pemfile::ec_private_keys(&mut Cursor::new(&tls_client_private_key)) + .map(|result| { + result + .map_err(|err| zerror!("Error processing client key: {err}.")) + .map(|key| PrivateKey(key.secret_sec1_der().to_vec())) + }) + .collect::, ZError>>()?; + } + + if keys.is_empty() { + bail!("No private key found for TLS client."); } let builder = ClientConfig::builder() @@ -765,57 +806,63 @@ 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 certs = rustls_pemfile::certs(&mut pem)?; - let trust_anchors = certs.iter().map(|cert| { - let ta = TrustAnchor::try_from_cert_der(&cert[..]).unwrap(); - OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject, - ta.spki, - ta.name_constraints, - ) - }); + let trust_anchors = process_pem(&mut pem)?; root_cert_store.add_trust_anchors(trust_anchors.into_iter()); 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 certs = rustls_pemfile::certs(&mut pem)?; - let trust_anchors = certs.iter().map(|cert| { - let ta = TrustAnchor::try_from_cert_der(&cert[..]).unwrap(); - OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject, - ta.spki, - ta.name_constraints, - ) - }); + let trust_anchors = process_pem(&mut pem)?; root_cert_store.add_trust_anchors(trust_anchors.into_iter()); 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 certs = rustls_pemfile::certs(&mut pem)?; - let trust_anchors = certs.iter().map(|cert| { - let ta = TrustAnchor::try_from_cert_der(&cert[..]).unwrap(); - OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject, - ta.spki, - ta.name_constraints, - ) - }); + let trust_anchors = process_pem(&mut pem)?; root_cert_store.add_trust_anchors(trust_anchors.into_iter()); 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>>()?; + + let owned_trust_anchors: Vec = trust_anchors + .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(|x| x.to_vec()), + ) + }) + .collect(); + + Ok(owned_trust_anchors) +} + fn load_default_webpki_certs() -> RootCertStore { let mut root_cert_store = RootCertStore::empty(); root_cert_store.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| { OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject, - ta.spki, - ta.name_constraints, + ta.subject.to_vec(), + ta.subject_public_key_info.to_vec(), + ta.name_constraints.clone().map(|x| x.to_vec()), ) })); root_cert_store