From 4cdb880d622de8a99116db5cbb900cb656cec3db Mon Sep 17 00:00:00 2001 From: Richard Patel Date: Fri, 13 Sep 2024 20:20:12 +0000 Subject: [PATCH] Always use ed25519_dalek for the QUIC-TLS cert verification - Ban use of other TLS signature algorithms such as RSA - Add boilerplate to replace ring's Ed25519 verifier with Dalek --- Cargo.lock | 1 + quic-client/src/nonblocking/quic_client.rs | 31 ++++---- streamer/Cargo.toml | 1 + streamer/src/nonblocking/testing_utilities.rs | 32 ++++----- streamer/src/quic.rs | 72 +++++++++++++------ 5 files changed, 79 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 992e5923e16309..4548444dbc95dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7848,6 +7848,7 @@ dependencies = [ "bytes", "crossbeam-channel", "dashmap", + "ed25519-dalek", "futures 0.3.30", "futures-util", "governor", diff --git a/quic-client/src/nonblocking/quic_client.rs b/quic-client/src/nonblocking/quic_client.rs index 352a3f0309ee48..fc65b81ab12492 100644 --- a/quic-client/src/nonblocking/quic_client.rs +++ b/quic-client/src/nonblocking/quic_client.rs @@ -28,7 +28,8 @@ use { transport::Result as TransportResult, }, solana_streamer::{ - nonblocking::quic::ALPN_TPU_PROTOCOL_ID, tls_certificates::new_dummy_x509_certificate, + nonblocking::quic::ALPN_TPU_PROTOCOL_ID, quic::TLS_SIGVERIFY_SCHEMES, + tls_certificates::new_dummy_x509_certificate, }, std::{ net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}, @@ -40,27 +41,24 @@ use { }; #[derive(Debug)] -pub struct SkipServerVerification(Arc); +pub struct SkipServerVerification; impl SkipServerVerification { pub fn new() -> Arc { - Arc::new(Self(Arc::new(rustls::crypto::ring::default_provider()))) + Arc::new(Self) } } impl rustls::client::danger::ServerCertVerifier for SkipServerVerification { fn verify_tls12_signature( &self, - message: &[u8], - cert: &rustls::pki_types::CertificateDer<'_>, - dss: &rustls::DigitallySignedStruct, + _message: &[u8], + _cert: &rustls::pki_types::CertificateDer<'_>, + _dss: &rustls::DigitallySignedStruct, ) -> Result { - rustls::crypto::verify_tls12_signature( - message, - cert, - dss, - &self.0.signature_verification_algorithms, - ) + Err(rustls::Error::PeerIncompatible( + rustls::PeerIncompatible::Tls13RequiredForQuic, + )) } fn verify_tls13_signature( @@ -69,16 +67,11 @@ impl rustls::client::danger::ServerCertVerifier for SkipServerVerification { cert: &rustls::pki_types::CertificateDer<'_>, dss: &rustls::DigitallySignedStruct, ) -> Result { - rustls::crypto::verify_tls13_signature( - message, - cert, - dss, - &self.0.signature_verification_algorithms, - ) + rustls::crypto::verify_tls13_signature(message, cert, dss, &TLS_SIGVERIFY_SCHEMES) } fn supported_verify_schemes(&self) -> Vec { - self.0.signature_verification_algorithms.supported_schemes() + TLS_SIGVERIFY_SCHEMES.supported_schemes() } fn verify_server_cert( diff --git a/streamer/Cargo.toml b/streamer/Cargo.toml index 873cb4459e3327..d72e43f5559e1f 100644 --- a/streamer/Cargo.toml +++ b/streamer/Cargo.toml @@ -13,6 +13,7 @@ edition = { workspace = true } async-channel = { workspace = true } bytes = { workspace = true } crossbeam-channel = { workspace = true } +ed25519-dalek = { workspace = true } dashmap = { workspace = true } futures = { workspace = true } futures-util = { workspace = true } diff --git a/streamer/src/nonblocking/testing_utilities.rs b/streamer/src/nonblocking/testing_utilities.rs index 4a63458e7c6d74..750ae0294f3390 100644 --- a/streamer/src/nonblocking/testing_utilities.rs +++ b/streamer/src/nonblocking/testing_utilities.rs @@ -5,7 +5,9 @@ use { DEFAULT_MAX_CONNECTIONS_PER_IPADDR_PER_MINUTE, DEFAULT_MAX_STREAMS_PER_MS, }, crate::{ - quic::{StreamerStats, MAX_STAKED_CONNECTIONS, MAX_UNSTAKED_CONNECTIONS}, + quic::{ + StreamerStats, MAX_STAKED_CONNECTIONS, MAX_UNSTAKED_CONNECTIONS, TLS_SIGVERIFY_SCHEMES, + }, streamer::StakedNodes, tls_certificates::new_dummy_x509_certificate, }, @@ -29,27 +31,24 @@ use { }; #[derive(Debug)] -pub struct SkipServerVerification(Arc); +pub struct SkipServerVerification; impl SkipServerVerification { pub fn new() -> Arc { - Arc::new(Self(Arc::new(rustls::crypto::ring::default_provider()))) + Arc::new(Self) } } impl rustls::client::danger::ServerCertVerifier for SkipServerVerification { fn verify_tls12_signature( &self, - message: &[u8], - cert: &rustls::pki_types::CertificateDer<'_>, - dss: &rustls::DigitallySignedStruct, + _message: &[u8], + _cert: &rustls::pki_types::CertificateDer<'_>, + _dss: &rustls::DigitallySignedStruct, ) -> Result { - rustls::crypto::verify_tls12_signature( - message, - cert, - dss, - &self.0.signature_verification_algorithms, - ) + Err(rustls::Error::PeerIncompatible( + rustls::PeerIncompatible::Tls13RequiredForQuic, + )) } fn verify_tls13_signature( @@ -58,16 +57,11 @@ impl rustls::client::danger::ServerCertVerifier for SkipServerVerification { cert: &rustls::pki_types::CertificateDer<'_>, dss: &rustls::DigitallySignedStruct, ) -> Result { - rustls::crypto::verify_tls13_signature( - message, - cert, - dss, - &self.0.signature_verification_algorithms, - ) + rustls::crypto::verify_tls13_signature(message, cert, dss, &TLS_SIGVERIFY_SCHEMES) } fn supported_verify_schemes(&self) -> Vec { - self.0.signature_verification_algorithms.supported_schemes() + TLS_SIGVERIFY_SCHEMES.supported_schemes() } fn verify_server_cert( diff --git a/streamer/src/quic.rs b/streamer/src/quic.rs index e9ca06a10bb133..0766b1b2904090 100644 --- a/streamer/src/quic.rs +++ b/streamer/src/quic.rs @@ -10,9 +10,13 @@ use { Endpoint, IdleTimeout, ServerConfig, }, rustls::{ - pki_types::{CertificateDer, UnixTime}, + crypto::WebPkiSupportedAlgorithms, + pki_types::{ + AlgorithmIdentifier, CertificateDer, InvalidSignature, SignatureVerificationAlgorithm, + UnixTime, + }, server::danger::ClientCertVerified, - DistinguishedName, KeyLogFile, + DistinguishedName, KeyLogFile, SignatureScheme, }, solana_perf::packet::PacketBatch, solana_sdk::{ @@ -38,12 +42,48 @@ pub const MAX_UNSTAKED_CONNECTIONS: usize = 500; // This will be adjusted and parameterized in follow-on PRs. pub const DEFAULT_QUIC_ENDPOINTS: usize = 1; +// Boilerplate required to use ed25519_dalek for signature verification. +#[derive(Debug)] +struct DalekEd25519; +static DALEK_ED25519: &dyn SignatureVerificationAlgorithm = &DalekEd25519; +const ED25519_ALG_ID: AlgorithmIdentifier = + AlgorithmIdentifier::from_slice(&[0x06, 0x03, 0x2b, 0x65, 0x70]); +impl SignatureVerificationAlgorithm for DalekEd25519 { + fn public_key_alg_id(&self) -> AlgorithmIdentifier { + ED25519_ALG_ID + } + + fn signature_alg_id(&self) -> AlgorithmIdentifier { + ED25519_ALG_ID + } + + fn verify_signature( + &self, + public_key: &[u8], + message: &[u8], + signature: &[u8], + ) -> Result<(), InvalidSignature> { + let publickey = + ed25519_dalek::PublicKey::from_bytes(public_key).map_err(|_| InvalidSignature)?; + let signature = + ed25519_dalek::Signature::try_from(signature).map_err(|_| InvalidSignature)?; + publickey + .verify(message, &signature) + .map_err(|_| InvalidSignature) + } +} + +pub static TLS_SIGVERIFY_SCHEMES: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms { + all: &[DALEK_ED25519], + mapping: &[(SignatureScheme::ED25519, &[DALEK_ED25519])], +}; + #[derive(Debug)] -pub struct SkipClientVerification(Arc); +pub struct SkipClientVerification; impl SkipClientVerification { pub fn new() -> Arc { - Arc::new(Self(Arc::new(rustls::crypto::ring::default_provider()))) + Arc::new(Self) } } @@ -69,16 +109,13 @@ impl rustls::server::danger::ClientCertVerifier for SkipClientVerification { fn verify_tls12_signature( &self, - message: &[u8], - cert: &rustls::pki_types::CertificateDer<'_>, - dss: &rustls::DigitallySignedStruct, + _message: &[u8], + _cert: &rustls::pki_types::CertificateDer<'_>, + _dss: &rustls::DigitallySignedStruct, ) -> Result { - rustls::crypto::verify_tls12_signature( - message, - cert, - dss, - &self.0.signature_verification_algorithms, - ) + Err(rustls::Error::PeerIncompatible( + rustls::PeerIncompatible::Tls13RequiredForQuic, + )) } fn verify_tls13_signature( @@ -87,16 +124,11 @@ impl rustls::server::danger::ClientCertVerifier for SkipClientVerification { cert: &rustls::pki_types::CertificateDer<'_>, dss: &rustls::DigitallySignedStruct, ) -> Result { - rustls::crypto::verify_tls13_signature( - message, - cert, - dss, - &self.0.signature_verification_algorithms, - ) + rustls::crypto::verify_tls13_signature(message, cert, dss, &TLS_SIGVERIFY_SCHEMES) } fn supported_verify_schemes(&self) -> Vec { - self.0.signature_verification_algorithms.supported_schemes() + TLS_SIGVERIFY_SCHEMES.supported_schemes() } fn offer_client_auth(&self) -> bool {