From 1d3c961289c2108886ad6571afb8d7e8e422ede0 Mon Sep 17 00:00:00 2001 From: Alex Pyattaev Date: Wed, 4 Dec 2024 02:22:42 +0200 Subject: [PATCH] Organize some of the duplicated TLS code into a separate crate (#3835) * Add a place to store common TLS-related code that has no other home * remove SkipServerVerification from tpu-client-next * move TLS manipulation stuff from streamer to solana-tls-utils * move SkipServerVerification, SkipClientVerification and ClientCertificate to solana-tls-utils to avoid duplication. --------- Co-authored-by: Alex Pyattaev --- Cargo.lock | 17 +++++ Cargo.toml | 4 +- core/Cargo.toml | 1 + core/src/repair/quic_endpoint.rs | 5 +- programs/sbf/Cargo.lock | 17 +++++ quic-client/Cargo.toml | 1 + quic-client/src/lib.rs | 7 +- quic-client/src/nonblocking/quic_client.rs | 64 +--------------- quic-client/tests/quic_client.rs | 6 +- streamer/Cargo.toml | 1 + streamer/src/lib.rs | 1 - streamer/src/nonblocking/quic.rs | 2 +- streamer/src/nonblocking/testing_utilities.rs | 56 +------------- streamer/src/quic.rs | 72 +----------------- svm/examples/Cargo.lock | 17 +++++ tls-utils/Cargo.toml | 22 ++++++ tls-utils/README | 1 + tls-utils/src/lib.rs | 14 ++++ .../src}/quic_client_certificate.rs | 2 +- tls-utils/src/skip_client_verification.rs | 73 +++++++++++++++++++ .../src}/skip_server_verification.rs | 4 +- .../src/tls_certificates.rs | 19 ++++- tpu-client-next/Cargo.toml | 1 + tpu-client-next/src/quic_networking.rs | 6 +- turbine/Cargo.toml | 1 + turbine/src/quic_endpoint.rs | 5 +- 26 files changed, 209 insertions(+), 210 deletions(-) create mode 100644 tls-utils/Cargo.toml create mode 100644 tls-utils/README create mode 100644 tls-utils/src/lib.rs rename {tpu-client-next/src/quic_networking => tls-utils/src}/quic_client_certificate.rs (90%) create mode 100644 tls-utils/src/skip_client_verification.rs rename {tpu-client-next/src/quic_networking => tls-utils/src}/skip_server_verification.rs (91%) rename {streamer => tls-utils}/src/tls_certificates.rs (89%) diff --git a/Cargo.lock b/Cargo.lock index 8d158f1bc1a12e..d183082c043e28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6734,6 +6734,7 @@ dependencies = [ "solana-svm", "solana-svm-transaction", "solana-timings", + "solana-tls-utils", "solana-tpu-client", "solana-transaction-status", "solana-turbine", @@ -8095,6 +8096,7 @@ dependencies = [ "solana-sdk", "solana-signer", "solana-streamer", + "solana-tls-utils", "solana-transaction-error", "thiserror 2.0.3", "tokio", @@ -8935,6 +8937,7 @@ dependencies = [ "solana-signer", "solana-streamer", "solana-time-utils", + "solana-tls-utils", "solana-transaction-error", "solana-transaction-metrics-tracker", "thiserror 2.0.3", @@ -9161,6 +9164,18 @@ dependencies = [ "solana-sdk", ] +[[package]] +name = "solana-tls-utils" +version = "2.2.0" +dependencies = [ + "rustls 0.23.19", + "solana-keypair", + "solana-pubkey", + "solana-sdk", + "solana-signer", + "x509-parser", +] + [[package]] name = "solana-tokens" version = "2.2.0" @@ -9256,6 +9271,7 @@ dependencies = [ "solana-rpc-client", "solana-sdk", "solana-streamer", + "solana-tls-utils", "solana-tpu-client", "thiserror 2.0.3", "tokio", @@ -9415,6 +9431,7 @@ dependencies = [ "solana-runtime", "solana-sdk", "solana-streamer", + "solana-tls-utils", "static_assertions", "test-case", "thiserror 2.0.3", diff --git a/Cargo.toml b/Cargo.toml index a234bfcedd9845..d8cd457ade48d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -191,6 +191,7 @@ members = [ "test-validator", "thin-client", "timings", + "tls-utils", "tokens", "tps-client", "tpu-client", @@ -527,8 +528,8 @@ solana-rent = { path = "sdk/rent", version = "=2.2.0", default-features = false solana-rent-debits = { path = "sdk/rent-debits", version = "=2.2.0" } solana-reserved-account-keys = { path = "sdk/reserved-account-keys", version = "=2.2.0", default-features = false } solana-reward-info = { path = "sdk/reward-info", version = "=2.2.0" } -solana-secp256r1-program = { path = "sdk/secp256r1-program", version = "=2.2.0", default-features = false } solana-sanitize = { path = "sdk/sanitize", version = "=2.2.0" } +solana-secp256r1-program = { path = "sdk/secp256r1-program", version = "=2.2.0", default-features = false } solana-seed-derivable = { path = "sdk/seed-derivable", version = "=2.2.0" } solana-seed-phrase = { path = "sdk/seed-phrase", version = "=2.2.0" } solana-serde = { path = "sdk/serde", version = "=2.2.0" } @@ -541,6 +542,7 @@ solana-slot-hashes = { path = "sdk/slot-hashes", version = "=2.2.0" } solana-slot-history = { path = "sdk/slot-history", version = "=2.2.0" } solana-time-utils = { path = "sdk/time-utils", version = "=2.2.0" } solana-timings = { path = "timings", version = "=2.2.0" } +solana-tls-utils = { path = "tls-utils", version = "=2.2.0" } solana-unified-scheduler-logic = { path = "unified-scheduler-logic", version = "=2.2.0" } solana-unified-scheduler-pool = { path = "unified-scheduler-pool", version = "=2.2.0" } solana-rpc = { path = "rpc", version = "=2.2.0" } diff --git a/core/Cargo.toml b/core/Cargo.toml index 0128e9fba35a85..df42ec84657648 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -83,6 +83,7 @@ solana-streamer = { workspace = true } solana-svm = { workspace = true } solana-svm-transaction = { workspace = true } solana-timings = { workspace = true } +solana-tls-utils = { workspace = true } solana-tpu-client = { workspace = true } solana-transaction-status = { workspace = true } solana-turbine = { workspace = true } diff --git a/core/src/repair/quic_endpoint.rs b/core/src/repair/quic_endpoint.rs index 0cdf11f1bfc8fe..e8085c522c8c2a 100644 --- a/core/src/repair/quic_endpoint.rs +++ b/core/src/repair/quic_endpoint.rs @@ -14,10 +14,11 @@ use { CertificateError, KeyLogFile, }, solana_gossip::contact_info::Protocol, - solana_quic_client::nonblocking::quic_client::SkipServerVerification, solana_runtime::bank_forks::BankForks, solana_sdk::{pubkey::Pubkey, signature::Keypair}, - solana_streamer::{quic::SkipClientVerification, tls_certificates::new_dummy_x509_certificate}, + solana_tls_utils::{ + new_dummy_x509_certificate, SkipClientVerification, SkipServerVerification, + }, std::{ cmp::Reverse, collections::{hash_map::Entry, HashMap}, diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 58449f5348fdf7..b5e9d8fe056f67 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -5427,6 +5427,7 @@ dependencies = [ "solana-svm", "solana-svm-transaction", "solana-timings", + "solana-tls-utils", "solana-tpu-client", "solana-transaction-status", "solana-turbine", @@ -6385,6 +6386,7 @@ dependencies = [ "solana-rpc-client-api", "solana-signer", "solana-streamer", + "solana-tls-utils", "solana-transaction-error", "thiserror 2.0.3", "tokio", @@ -7545,6 +7547,7 @@ dependencies = [ "solana-signature", "solana-signer", "solana-time-utils", + "solana-tls-utils", "solana-transaction-error", "solana-transaction-metrics-tracker", "thiserror 2.0.3", @@ -7726,6 +7729,18 @@ dependencies = [ "solana-sdk", ] +[[package]] +name = "solana-tls-utils" +version = "2.2.0" +dependencies = [ + "rustls 0.23.19", + "solana-keypair", + "solana-pubkey", + "solana-sdk", + "solana-signer", + "x509-parser", +] + [[package]] name = "solana-tpu-client" version = "2.2.0" @@ -7763,6 +7778,7 @@ dependencies = [ "solana-rpc-client", "solana-sdk", "solana-streamer", + "solana-tls-utils", "solana-tpu-client", "thiserror 2.0.3", "tokio", @@ -7884,6 +7900,7 @@ dependencies = [ "solana-runtime", "solana-sdk", "solana-streamer", + "solana-tls-utils", "static_assertions", "thiserror 2.0.3", "tokio", diff --git a/quic-client/Cargo.toml b/quic-client/Cargo.toml index c6eeab8d842fc7..3337a17950c869 100644 --- a/quic-client/Cargo.toml +++ b/quic-client/Cargo.toml @@ -29,6 +29,7 @@ solana-quic-definitions = { workspace = true } solana-rpc-client-api = { workspace = true } solana-signer = { workspace = true } solana-streamer = { workspace = true } +solana-tls-utils = { workspace = true } solana-transaction-error = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true, features = ["full"] } diff --git a/quic-client/src/lib.rs b/quic-client/src/lib.rs index 1d6c7b02d1dfb1..c0d2ac0f7a8f9b 100644 --- a/quic-client/src/lib.rs +++ b/quic-client/src/lib.rs @@ -9,8 +9,8 @@ extern crate solana_metrics; use { crate::{ nonblocking::quic_client::{ - QuicClient, QuicClientCertificate, - QuicClientConnection as NonblockingQuicClientConnection, QuicLazyInitializedEndpoint, + QuicClient, QuicClientConnection as NonblockingQuicClientConnection, + QuicLazyInitializedEndpoint, }, quic_client::QuicClientConnection as BlockingQuicClientConnection, }, @@ -25,7 +25,8 @@ use { solana_keypair::Keypair, solana_pubkey::Pubkey, solana_signer::Signer, - solana_streamer::{streamer::StakedNodes, tls_certificates::new_dummy_x509_certificate}, + solana_streamer::streamer::StakedNodes, + solana_tls_utils::{new_dummy_x509_certificate, QuicClientCertificate}, std::{ net::{IpAddr, SocketAddr}, sync::{Arc, RwLock}, diff --git a/quic-client/src/nonblocking/quic_client.rs b/quic-client/src/nonblocking/quic_client.rs index 1fae67c6e69ce0..2c9020bf846835 100644 --- a/quic-client/src/nonblocking/quic_client.rs +++ b/quic-client/src/nonblocking/quic_client.rs @@ -22,9 +22,8 @@ use { QUIC_CONNECTION_HANDSHAKE_TIMEOUT, QUIC_KEEP_ALIVE, QUIC_MAX_TIMEOUT, }, solana_rpc_client_api::client_error::ErrorKind as ClientErrorKind, - solana_streamer::{ - nonblocking::quic::ALPN_TPU_PROTOCOL_ID, tls_certificates::new_dummy_x509_certificate, - }, + solana_streamer::nonblocking::quic::ALPN_TPU_PROTOCOL_ID, + solana_tls_utils::{new_dummy_x509_certificate, QuicClientCertificate, SkipServerVerification}, solana_transaction_error::TransportResult, std::{ net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}, @@ -35,65 +34,6 @@ use { tokio::{sync::OnceCell, time::timeout}, }; -#[derive(Debug)] -pub struct SkipServerVerification(Arc); - -impl SkipServerVerification { - pub fn new() -> Arc { - Arc::new(Self(Arc::new(rustls::crypto::ring::default_provider()))) - } -} - -impl rustls::client::danger::ServerCertVerifier for SkipServerVerification { - fn verify_tls12_signature( - &self, - message: &[u8], - cert: &rustls::pki_types::CertificateDer<'_>, - dss: &rustls::DigitallySignedStruct, - ) -> Result { - rustls::crypto::verify_tls12_signature( - message, - cert, - dss, - &self.0.signature_verification_algorithms, - ) - } - - fn verify_tls13_signature( - &self, - message: &[u8], - cert: &rustls::pki_types::CertificateDer<'_>, - dss: &rustls::DigitallySignedStruct, - ) -> Result { - rustls::crypto::verify_tls13_signature( - message, - cert, - dss, - &self.0.signature_verification_algorithms, - ) - } - - fn supported_verify_schemes(&self) -> Vec { - self.0.signature_verification_algorithms.supported_schemes() - } - - fn verify_server_cert( - &self, - _end_entity: &rustls::pki_types::CertificateDer<'_>, - _intermediates: &[rustls::pki_types::CertificateDer<'_>], - _server_name: &rustls::pki_types::ServerName<'_>, - _ocsp_response: &[u8], - _now: rustls::pki_types::UnixTime, - ) -> Result { - Ok(rustls::client::danger::ServerCertVerified::assertion()) - } -} - -pub struct QuicClientCertificate { - pub certificate: rustls::pki_types::CertificateDer<'static>, - pub key: rustls::pki_types::PrivateKeyDer<'static>, -} - /// A lazy-initialized Quic Endpoint pub struct QuicLazyInitializedEndpoint { endpoint: OnceCell>, diff --git a/quic-client/tests/quic_client.rs b/quic-client/tests/quic_client.rs index 9dc5c80c76f61d..65cf3d2fffa6ac 100644 --- a/quic-client/tests/quic_client.rs +++ b/quic-client/tests/quic_client.rs @@ -6,15 +6,13 @@ mod tests { solana_connection_cache::connection_cache_stats::ConnectionCacheStats, solana_net_utils::bind_to_localhost, solana_perf::packet::PacketBatch, - solana_quic_client::nonblocking::quic_client::{ - QuicClientCertificate, QuicLazyInitializedEndpoint, - }, + solana_quic_client::nonblocking::quic_client::QuicLazyInitializedEndpoint, solana_sdk::{packet::PACKET_DATA_SIZE, signature::Keypair}, solana_streamer::{ quic::{QuicServerParams, SpawnServerResult}, streamer::StakedNodes, - tls_certificates::new_dummy_x509_certificate, }, + solana_tls_utils::{new_dummy_x509_certificate, QuicClientCertificate}, std::{ net::{SocketAddr, UdpSocket}, sync::{ diff --git a/streamer/Cargo.toml b/streamer/Cargo.toml index 6466a22842a93b..54e0ce41d1b2ae 100644 --- a/streamer/Cargo.toml +++ b/streamer/Cargo.toml @@ -42,6 +42,7 @@ solana-quic-definitions = { workspace = true } solana-signature = { workspace = true } solana-signer = { workspace = true } solana-time-utils = { workspace = true } +solana-tls-utils = { workspace = true } solana-transaction-error = { workspace = true } solana-transaction-metrics-tracker = { workspace = true } thiserror = { workspace = true } diff --git a/streamer/src/lib.rs b/streamer/src/lib.rs index 225515646e8e0d..3c6979a05e1c7e 100644 --- a/streamer/src/lib.rs +++ b/streamer/src/lib.rs @@ -6,7 +6,6 @@ pub mod recvmmsg; pub mod sendmmsg; pub mod socket; pub mod streamer; -pub mod tls_certificates; #[macro_use] extern crate log; diff --git a/streamer/src/nonblocking/quic.rs b/streamer/src/nonblocking/quic.rs index a840c5b8fea94f..002a00bb12674a 100644 --- a/streamer/src/nonblocking/quic.rs +++ b/streamer/src/nonblocking/quic.rs @@ -9,7 +9,6 @@ use { }, quic::{configure_server, QuicServerError, QuicServerParams, StreamerStats}, streamer::StakedNodes, - tls_certificates::get_pubkey_from_tls_certificate, }, async_channel::{ unbounded as async_unbounded, Receiver as AsyncReceiver, Sender as AsyncSender, @@ -36,6 +35,7 @@ use { }, solana_signature::Signature, solana_time_utils as timing, + solana_tls_utils::get_pubkey_from_tls_certificate, solana_transaction_metrics_tracker::signature_if_should_track_packet, std::{ array, diff --git a/streamer/src/nonblocking/testing_utilities.rs b/streamer/src/nonblocking/testing_utilities.rs index 23ba7a3e0bac14..80c758ca8957af 100644 --- a/streamer/src/nonblocking/testing_utilities.rs +++ b/streamer/src/nonblocking/testing_utilities.rs @@ -11,7 +11,6 @@ use { MAX_UNSTAKED_CONNECTIONS, }, streamer::StakedNodes, - tls_certificates::new_dummy_x509_certificate, }, crossbeam_channel::unbounded, quinn::{ @@ -22,6 +21,7 @@ use { solana_net_utils::bind_to_localhost, solana_perf::packet::PacketBatch, solana_quic_definitions::{QUIC_KEEP_ALIVE, QUIC_MAX_TIMEOUT}, + solana_tls_utils::{new_dummy_x509_certificate, SkipServerVerification}, std::{ net::{SocketAddr, UdpSocket}, sync::{atomic::AtomicBool, Arc, RwLock}, @@ -29,60 +29,6 @@ use { tokio::task::JoinHandle, }; -#[derive(Debug)] -pub struct SkipServerVerification(Arc); - -impl SkipServerVerification { - pub fn new() -> Arc { - Arc::new(Self(Arc::new(rustls::crypto::ring::default_provider()))) - } -} - -impl rustls::client::danger::ServerCertVerifier for SkipServerVerification { - fn verify_tls12_signature( - &self, - message: &[u8], - cert: &rustls::pki_types::CertificateDer<'_>, - dss: &rustls::DigitallySignedStruct, - ) -> Result { - rustls::crypto::verify_tls12_signature( - message, - cert, - dss, - &self.0.signature_verification_algorithms, - ) - } - - fn verify_tls13_signature( - &self, - message: &[u8], - cert: &rustls::pki_types::CertificateDer<'_>, - dss: &rustls::DigitallySignedStruct, - ) -> Result { - rustls::crypto::verify_tls13_signature( - message, - cert, - dss, - &self.0.signature_verification_algorithms, - ) - } - - fn supported_verify_schemes(&self) -> Vec { - self.0.signature_verification_algorithms.supported_schemes() - } - - fn verify_server_cert( - &self, - _end_entity: &rustls::pki_types::CertificateDer<'_>, - _intermediates: &[rustls::pki_types::CertificateDer<'_>], - _server_name: &rustls::pki_types::ServerName<'_>, - _ocsp_response: &[u8], - _now: rustls::pki_types::UnixTime, - ) -> Result { - Ok(rustls::client::danger::ServerCertVerified::assertion()) - } -} - pub fn get_client_config(keypair: &Keypair) -> ClientConfig { let (cert, key) = new_dummy_x509_certificate(keypair); diff --git a/streamer/src/quic.rs b/streamer/src/quic.rs index f6b7778448582f..1914861047dc43 100644 --- a/streamer/src/quic.rs +++ b/streamer/src/quic.rs @@ -5,7 +5,6 @@ use { DEFAULT_MAX_STREAMS_PER_MS, DEFAULT_WAIT_FOR_CHUNK_TIMEOUT, }, streamer::StakedNodes, - tls_certificates::new_dummy_x509_certificate, }, crossbeam_channel::Sender, pem::Pem, @@ -13,17 +12,14 @@ use { crypto::rustls::{NoInitialCipherSuite, QuicServerConfig}, Endpoint, IdleTimeout, ServerConfig, }, - rustls::{ - pki_types::{CertificateDer, UnixTime}, - server::danger::ClientCertVerified, - DistinguishedName, KeyLogFile, - }, + rustls::KeyLogFile, solana_keypair::Keypair, solana_packet::PACKET_DATA_SIZE, solana_perf::packet::PacketBatch, solana_quic_definitions::{ NotifyKeyUpdate, QUIC_MAX_TIMEOUT, QUIC_MAX_UNSTAKED_CONCURRENT_STREAMS, }, + solana_tls_utils::{new_dummy_x509_certificate, SkipClientVerification}, std::{ net::UdpSocket, sync::{ @@ -44,76 +40,12 @@ pub const DEFAULT_QUIC_ENDPOINTS: usize = 1; // inlined to avoid solana-sdk dep pub(crate) const DEFAULT_TPU_COALESCE: Duration = Duration::from_millis(5); -#[derive(Debug)] -pub struct SkipClientVerification(Arc); - -impl SkipClientVerification { - pub fn new() -> Arc { - Arc::new(Self(Arc::new(rustls::crypto::ring::default_provider()))) - } -} - pub struct SpawnServerResult { pub endpoints: Vec, pub thread: thread::JoinHandle<()>, pub key_updater: Arc, } -impl rustls::server::danger::ClientCertVerifier for SkipClientVerification { - fn verify_client_cert( - &self, - _end_entity: &CertificateDer, - _intermediates: &[CertificateDer], - _now: UnixTime, - ) -> Result { - Ok(rustls::server::danger::ClientCertVerified::assertion()) - } - - fn root_hint_subjects(&self) -> &[DistinguishedName] { - &[] - } - - fn verify_tls12_signature( - &self, - message: &[u8], - cert: &rustls::pki_types::CertificateDer<'_>, - dss: &rustls::DigitallySignedStruct, - ) -> Result { - rustls::crypto::verify_tls12_signature( - message, - cert, - dss, - &self.0.signature_verification_algorithms, - ) - } - - fn verify_tls13_signature( - &self, - message: &[u8], - cert: &rustls::pki_types::CertificateDer<'_>, - dss: &rustls::DigitallySignedStruct, - ) -> Result { - rustls::crypto::verify_tls13_signature( - message, - cert, - dss, - &self.0.signature_verification_algorithms, - ) - } - - fn supported_verify_schemes(&self) -> Vec { - self.0.signature_verification_algorithms.supported_schemes() - } - - fn offer_client_auth(&self) -> bool { - true - } - - fn client_auth_mandatory(&self) -> bool { - self.offer_client_auth() - } -} - /// Returns default server configuration along with its PEM certificate chain. #[allow(clippy::field_reassign_with_default)] // https://github.com/rust-lang/rust-clippy/issues/6527 pub(crate) fn configure_server( diff --git a/svm/examples/Cargo.lock b/svm/examples/Cargo.lock index d768fff746d0a5..870abcd3eedb7a 100644 --- a/svm/examples/Cargo.lock +++ b/svm/examples/Cargo.lock @@ -5278,6 +5278,7 @@ dependencies = [ "solana-svm", "solana-svm-transaction", "solana-timings", + "solana-tls-utils", "solana-tpu-client", "solana-transaction-status", "solana-turbine", @@ -6205,6 +6206,7 @@ dependencies = [ "solana-rpc-client-api", "solana-signer", "solana-streamer", + "solana-tls-utils", "solana-transaction-error", "thiserror 2.0.3", "tokio", @@ -6873,6 +6875,7 @@ dependencies = [ "solana-signature", "solana-signer", "solana-time-utils", + "solana-tls-utils", "solana-transaction-error", "solana-transaction-metrics-tracker", "thiserror 2.0.3", @@ -7071,6 +7074,18 @@ dependencies = [ "solana-sdk", ] +[[package]] +name = "solana-tls-utils" +version = "2.2.0" +dependencies = [ + "rustls 0.23.19", + "solana-keypair", + "solana-pubkey", + "solana-sdk", + "solana-signer", + "x509-parser", +] + [[package]] name = "solana-tpu-client" version = "2.2.0" @@ -7108,6 +7123,7 @@ dependencies = [ "solana-rpc-client", "solana-sdk", "solana-streamer", + "solana-tls-utils", "solana-tpu-client", "thiserror 2.0.3", "tokio", @@ -7229,6 +7245,7 @@ dependencies = [ "solana-runtime", "solana-sdk", "solana-streamer", + "solana-tls-utils", "static_assertions", "thiserror 2.0.3", "tokio", diff --git a/tls-utils/Cargo.toml b/tls-utils/Cargo.toml new file mode 100644 index 00000000000000..e244e361b46ca0 --- /dev/null +++ b/tls-utils/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "solana-tls-utils" +description = "Solana TLS utilities" +documentation = "https://docs.rs/solana-tls-utils" +publish = false +version = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +edition = { workspace = true } + +[dependencies] +rustls = { workspace = true, features = ["ring"] } +solana-keypair = { workspace = true } +solana-pubkey = { workspace = true } +solana-sdk = { workspace = true } +solana-signer = { workspace = true } +x509-parser = { workspace = true } + +[lints] +workspace = true diff --git a/tls-utils/README b/tls-utils/README new file mode 100644 index 00000000000000..8c42c2a1c875f8 --- /dev/null +++ b/tls-utils/README @@ -0,0 +1 @@ +A collection of utility functions and structures needed to bridge the conceptual gap between conventional TLS security models in protocols like QUIC and Agave use cases diff --git a/tls-utils/src/lib.rs b/tls-utils/src/lib.rs new file mode 100644 index 00000000000000..2985d127fd379a --- /dev/null +++ b/tls-utils/src/lib.rs @@ -0,0 +1,14 @@ +//! Collection of TLS related code fragments that end up popping up everywhere where quic is used. +//! Aggregated here to avoid bugs due to conflicting implementations of the same functionality. + +mod tls_certificates; +pub use tls_certificates::*; + +mod quic_client_certificate; +pub use quic_client_certificate::*; + +mod skip_server_verification; +pub use skip_server_verification::SkipServerVerification; + +mod skip_client_verification; +pub use skip_client_verification::SkipClientVerification; diff --git a/tpu-client-next/src/quic_networking/quic_client_certificate.rs b/tls-utils/src/quic_client_certificate.rs similarity index 90% rename from tpu-client-next/src/quic_networking/quic_client_certificate.rs rename to tls-utils/src/quic_client_certificate.rs index b9d2eca4a51f4f..b49f27dba36352 100644 --- a/tpu-client-next/src/quic_networking/quic_client_certificate.rs +++ b/tls-utils/src/quic_client_certificate.rs @@ -1,7 +1,7 @@ use { + crate::new_dummy_x509_certificate, rustls::pki_types::{CertificateDer, PrivateKeyDer}, solana_sdk::signature::Keypair, - solana_streamer::tls_certificates::new_dummy_x509_certificate, }; pub struct QuicClientCertificate { diff --git a/tls-utils/src/skip_client_verification.rs b/tls-utils/src/skip_client_verification.rs new file mode 100644 index 00000000000000..92be37ab8be657 --- /dev/null +++ b/tls-utils/src/skip_client_verification.rs @@ -0,0 +1,73 @@ +use { + rustls::{ + pki_types::{CertificateDer, UnixTime}, + server::danger::ClientCertVerified, + DistinguishedName, + }, + std::{fmt::Debug, sync::Arc}, +}; + +/// Implementation of [`ClientCertVerifier`] that ignores the server +/// certificate. Yet still checks the TLS signatures. +#[derive(Debug)] +pub struct SkipClientVerification(Arc); + +impl SkipClientVerification { + pub fn new() -> Arc { + Arc::new(Self(Arc::new(rustls::crypto::ring::default_provider()))) + } +} +impl rustls::server::danger::ClientCertVerifier for SkipClientVerification { + fn verify_client_cert( + &self, + _end_entity: &CertificateDer, + _intermediates: &[CertificateDer], + _now: UnixTime, + ) -> Result { + Ok(rustls::server::danger::ClientCertVerified::assertion()) + } + + fn root_hint_subjects(&self) -> &[DistinguishedName] { + &[] + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &rustls::pki_types::CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result { + rustls::crypto::verify_tls12_signature( + message, + cert, + dss, + &self.0.signature_verification_algorithms, + ) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &rustls::pki_types::CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result { + rustls::crypto::verify_tls13_signature( + message, + cert, + dss, + &self.0.signature_verification_algorithms, + ) + } + + fn supported_verify_schemes(&self) -> Vec { + self.0.signature_verification_algorithms.supported_schemes() + } + + fn offer_client_auth(&self) -> bool { + true + } + + fn client_auth_mandatory(&self) -> bool { + self.offer_client_auth() + } +} diff --git a/tpu-client-next/src/quic_networking/skip_server_verification.rs b/tls-utils/src/skip_server_verification.rs similarity index 91% rename from tpu-client-next/src/quic_networking/skip_server_verification.rs rename to tls-utils/src/skip_server_verification.rs index 70b17e0fcd5bab..cac5dd59410825 100644 --- a/tpu-client-next/src/quic_networking/skip_server_verification.rs +++ b/tls-utils/src/skip_server_verification.rs @@ -13,7 +13,9 @@ use { /// Implementation of [`ServerCertVerifier`] that ignores the server /// certificate. Yet still checks the TLS signatures. -pub(crate) struct SkipServerVerification(Arc); +/// This is useful for turbine (where server verification is not feasible) and for tests +/// Logic mostly copied from rustls examples. +pub struct SkipServerVerification(Arc); impl SkipServerVerification { pub fn new() -> Arc { diff --git a/streamer/src/tls_certificates.rs b/tls-utils/src/tls_certificates.rs similarity index 89% rename from streamer/src/tls_certificates.rs rename to tls-utils/src/tls_certificates.rs index d48b8608a44b70..96607601edee09 100644 --- a/streamer/src/tls_certificates.rs +++ b/tls-utils/src/tls_certificates.rs @@ -29,9 +29,22 @@ pub fn new_dummy_x509_certificate( 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, 0x20, ]; - let mut key_pkcs8_der = Vec::::with_capacity(PKCS8_PREFIX.len() + 32); - key_pkcs8_der.extend_from_slice(&PKCS8_PREFIX); - key_pkcs8_der.extend_from_slice(keypair.secret().as_bytes()); + + let key_pkcs8_der = { + let keypair_secret_bytes = keypair.secret().as_bytes(); + let keypair_secret_len = keypair_secret_bytes.len(); + if keypair_secret_len != 32 { + panic!("Unexpected secret key length!"); + } + let buffer_size = PKCS8_PREFIX + .len() + .checked_add(keypair_secret_len) //clippy being overly guarded here but optimizer will elide checked_add + .expect("Unexpected secret key length!"); + let mut key_pkcs8_der = Vec::::with_capacity(buffer_size); + key_pkcs8_der.extend_from_slice(&PKCS8_PREFIX); + key_pkcs8_der.extend_from_slice(keypair_secret_bytes); + key_pkcs8_der + }; // Create a dummy certificate. Only the SubjectPublicKeyInfo field // is relevant to the peer-to-peer protocols. The signature of the diff --git a/tpu-client-next/Cargo.toml b/tpu-client-next/Cargo.toml index 88739eb79f6a04..64a3ae58031982 100644 --- a/tpu-client-next/Cargo.toml +++ b/tpu-client-next/Cargo.toml @@ -20,6 +20,7 @@ solana-measure = { workspace = true } solana-rpc-client = { workspace = true } solana-sdk = { workspace = true } solana-streamer = { workspace = true } +solana-tls-utils = { workspace = true } solana-tpu-client = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true } diff --git a/tpu-client-next/src/quic_networking.rs b/tpu-client-next/src/quic_networking.rs index 4de596ca2e3bdb..65c3b07139f5f3 100644 --- a/tpu-client-next/src/quic_networking.rs +++ b/tpu-client-next/src/quic_networking.rs @@ -5,19 +5,17 @@ use { crypto::rustls::QuicClientConfig, ClientConfig, Connection, Endpoint, IdleTimeout, TransportConfig, }, - skip_server_verification::SkipServerVerification, solana_sdk::quic::{QUIC_KEEP_ALIVE, QUIC_MAX_TIMEOUT}, solana_streamer::nonblocking::quic::ALPN_TPU_PROTOCOL_ID, + solana_tls_utils::SkipServerVerification, std::{net::SocketAddr, sync::Arc}, }; pub mod error; -pub mod quic_client_certificate; -pub mod skip_server_verification; pub use { error::{IoErrorWithPartialEq, QuicError}, - quic_client_certificate::QuicClientCertificate, + solana_tls_utils::QuicClientCertificate, }; pub(crate) fn create_client_config(client_certificate: QuicClientCertificate) -> ClientConfig { diff --git a/turbine/Cargo.toml b/turbine/Cargo.toml index 083b18e9c55e33..4d6ce063a3e79e 100644 --- a/turbine/Cargo.toml +++ b/turbine/Cargo.toml @@ -40,6 +40,7 @@ solana-rpc-client-api = { workspace = true } solana-runtime = { workspace = true } solana-sdk = { workspace = true } solana-streamer = { workspace = true } +solana-tls-utils = { workspace = true } static_assertions = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true } diff --git a/turbine/src/quic_endpoint.rs b/turbine/src/quic_endpoint.rs index 50a9cb4cdf72b4..175663bfa9fe2a 100644 --- a/turbine/src/quic_endpoint.rs +++ b/turbine/src/quic_endpoint.rs @@ -13,10 +13,11 @@ use { pki_types::{CertificateDer, PrivateKeyDer}, CertificateError, KeyLogFile, }, - solana_quic_client::nonblocking::quic_client::SkipServerVerification, solana_runtime::bank_forks::BankForks, solana_sdk::{pubkey::Pubkey, signature::Keypair}, - solana_streamer::{quic::SkipClientVerification, tls_certificates::new_dummy_x509_certificate}, + solana_tls_utils::{ + new_dummy_x509_certificate, SkipClientVerification, SkipServerVerification, + }, std::{ cmp::Reverse, collections::{hash_map::Entry, HashMap},