From ce8e7426729aca3a6d19f4843311cc4d591ecd49 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 20 Oct 2022 11:52:35 +0400 Subject: [PATCH] make certificate an argument in `Transport::new` by analogue with `id_keys`. See https://github.com/paritytech/substrate/pull/12529#discussion_r999581906 Both peer's identity and certificate will need to be stored somewhere (disk) and reused across restarts. Since this is a dominant use-case, I've switched `Config` API to accept a certificate and opted out of generating one completely. --- transports/webrtc/src/tokio/transport.rs | 82 +++++++++++++++--------- transports/webrtc/tests/smoke.rs | 18 ++++-- 2 files changed, 66 insertions(+), 34 deletions(-) diff --git a/transports/webrtc/src/tokio/transport.rs b/transports/webrtc/src/tokio/transport.rs index b9aa35b8b34..7fe7be47de0 100644 --- a/transports/webrtc/src/tokio/transport.rs +++ b/transports/webrtc/src/tokio/transport.rs @@ -26,7 +26,6 @@ use libp2p_core::{ transport::{ListenerId, TransportError, TransportEvent}, PeerId, }; -use rand::distributions::DistString; use webrtc::peer_connection::certificate::RTCCertificate; use webrtc::peer_connection::configuration::RTCConfiguration; @@ -47,20 +46,37 @@ use crate::tokio::{ /// A WebRTC transport with direct p2p communication (without a STUN server). pub struct Transport { - /// The config which holds this peer's certificate(s). + /// The config which holds this peer's keys and certificate. config: Config, - /// `Keypair` identifying this peer - id_keys: identity::Keypair, /// All the active listeners. listeners: SelectAll, } impl Transport { /// Creates a new WebRTC transport. - pub fn new(id_keys: identity::Keypair) -> Self { + /// + /// # Example + /// + /// ``` + /// use libp2p_core::identity; + /// use webrtc::peer_connection::certificate::RTCCertificate; + /// use rand::distributions::DistString; + /// use libp2p_webrtc::tokio::Transport; + /// + /// let id_keys = identity::Keypair::generate_ed25519(); + /// let certificate = { + /// let mut params = rcgen::CertificateParams::new(vec![ + /// rand::distributions::Alphanumeric.sample_string(&mut rand::thread_rng(), 16) + /// ]); + /// params.alg = &rcgen::PKCS_ECDSA_P256_SHA256; + /// RTCCertificate::from_params(params).expect("default params to work") + /// }; + /// + /// let transport = Transport::new(id_keys, certificate); + /// ``` + pub fn new(id_keys: identity::Keypair, certificate: RTCCertificate) -> Self { Self { - config: Config::new(), - id_keys, + config: Config::new(id_keys, certificate), listeners: SelectAll::new(), } } @@ -84,7 +100,6 @@ impl libp2p_core::Transport for Transport { id, self.config.clone(), udp_mux, - self.id_keys.clone(), IfWatcher::new().map_err(|io| TransportError::Other(Error::Io(io)))?, )); @@ -120,7 +135,6 @@ impl libp2p_core::Transport for Transport { let config = self.config.clone(); let client_fingerprint = self.config.fingerprint; - let id_keys = self.id_keys.clone(); let udp_mux = self .listeners .iter() @@ -136,7 +150,7 @@ impl libp2p_core::Transport for Transport { udp_mux, client_fingerprint, server_fingerprint, - id_keys, + config.id_keys, ) .await?; @@ -177,9 +191,6 @@ struct ListenStream { /// The UDP muxer that manages all ICE connections. udp_mux: UDPMuxNewAddr, - /// `Keypair` identifying this peer - id_keys: identity::Keypair, - /// Set to `Some` if this listener should close. /// /// Optionally contains a [`TransportEvent::ListenerClosed`] that should be @@ -200,7 +211,6 @@ impl ListenStream { listener_id: ListenerId, config: Config, udp_mux: UDPMuxNewAddr, - id_keys: identity::Keypair, if_watcher: IfWatcher, ) -> Self { ListenStream { @@ -208,7 +218,6 @@ impl ListenStream { listen_addr: udp_mux.listen_addr(), config, udp_mux, - id_keys, report_closed: None, if_watcher, } @@ -305,7 +314,7 @@ impl Stream for ListenStream { self.udp_mux.udp_mux_handle(), self.config.fingerprint, new_addr.ufrag, - self.id_keys.clone(), + self.config.id_keys.clone(), ) .boxed(); @@ -324,31 +333,36 @@ impl Stream for ListenStream { } } +/// A config which holds peer's keys and a x509Cert used to authenticate WebRTC communications. #[derive(Clone)] struct Config { inner: RTCConfiguration, fingerprint: Fingerprint, + id_keys: identity::Keypair, } impl Config { - fn new() -> Self { - let mut params = rcgen::CertificateParams::new(vec![ - rand::distributions::Alphanumeric.sample_string(&mut rand::thread_rng(), 16) - ]); - params.alg = &rcgen::PKCS_ECDSA_P256_SHA256; - let certificate = RTCCertificate::from_params(params).expect("default params to work"); - - let fingerprints = certificate.get_fingerprints().expect("to never fail"); // TODO: Remove `Result` upstream? + /// Returns a new [`Config`] with the given keys and certificate. + /// + /// # Panics + /// + /// This function will panic if there's no fingerprint with the SHA-256 algorithm (see + /// [`RTCCertificate::get_fingerprints`]). + fn new(id_keys: identity::Keypair, certificate: RTCCertificate) -> Self { + let fingerprints = certificate.get_fingerprints().expect("to never fail"); + let sha256_fingerprint = fingerprints + .iter() + .find(|f| f.algorithm == "sha-256") + .expect("a SHA-256 fingerprint"); Self { + id_keys, inner: RTCConfiguration { certificates: vec![certificate], ..RTCConfiguration::default() }, - fingerprint: Fingerprint::try_from_rtc_dtls( - fingerprints.first().expect("at least one certificate"), - ) - .expect("we specified SHA-256"), + fingerprint: Fingerprint::try_from_rtc_dtls(sha256_fingerprint) + .expect("we specified SHA-256"), } } } @@ -435,6 +449,7 @@ mod tests { use super::*; use futures::future::poll_fn; use libp2p_core::{multiaddr::Protocol, Transport as _}; + use rand::distributions::DistString; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; #[test] @@ -535,7 +550,8 @@ mod tests { #[tokio::test] async fn close_listener() { let id_keys = identity::Keypair::generate_ed25519(); - let mut transport = Transport::new(id_keys); + let certificate = generate_certificate(); + let mut transport = Transport::new(id_keys, certificate); assert!(poll_fn(|cx| Pin::new(&mut transport).as_mut().poll(cx)) .now_or_never() @@ -584,4 +600,12 @@ mod tests { assert!(transport.listeners.is_empty()); } } + + fn generate_certificate() -> RTCCertificate { + let mut params = rcgen::CertificateParams::new(vec![ + rand::distributions::Alphanumeric.sample_string(&mut rand::thread_rng(), 16) + ]); + params.alg = &rcgen::PKCS_ECDSA_P256_SHA256; + RTCCertificate::from_params(params).expect("default params to work") + } } diff --git a/transports/webrtc/tests/smoke.rs b/transports/webrtc/tests/smoke.rs index 145646ae1f8..338fd98f36d 100644 --- a/transports/webrtc/tests/smoke.rs +++ b/transports/webrtc/tests/smoke.rs @@ -18,6 +18,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +use ::webrtc::peer_connection::certificate::RTCCertificate; use anyhow::Result; use async_trait::async_trait; use futures::{ @@ -32,6 +33,7 @@ use libp2p::request_response::{ }; use libp2p::swarm::{Swarm, SwarmBuilder, SwarmEvent}; use libp2p::webrtc::tokio as webrtc; +use rand::distributions::DistString; use rand::RngCore; use std::{io, iter}; @@ -468,9 +470,11 @@ impl RequestResponseCodec for PingCodec { } fn create_swarm() -> Result>> { - let keypair = generate_tls_keypair(); - let peer_id = keypair.public().to_peer_id(); - let transport = webrtc::Transport::new(keypair); + let id_keys = identity::Keypair::generate_ed25519(); + let peer_id = id_keys.public().to_peer_id(); + let certificate = generate_certificate(); + let transport = webrtc::Transport::new(id_keys, certificate); + let protocols = iter::once((PingProtocol(), ProtocolSupport::Full)); let cfg = RequestResponseConfig::default(); let behaviour = RequestResponse::new(PingCodec(), protocols, cfg); @@ -485,6 +489,10 @@ fn create_swarm() -> Result>> { .build()) } -fn generate_tls_keypair() -> identity::Keypair { - identity::Keypair::generate_ed25519() +fn generate_certificate() -> RTCCertificate { + let mut params = rcgen::CertificateParams::new(vec![ + rand::distributions::Alphanumeric.sample_string(&mut rand::thread_rng(), 16) + ]); + params.alg = &rcgen::PKCS_ECDSA_P256_SHA256; + RTCCertificate::from_params(params).expect("default params to work") }