fedimint_server/net/p2p_connector/
tls.rs

1use std::collections::BTreeMap;
2use std::net::SocketAddr;
3use std::sync::Arc;
4
5use anyhow::{Context as _, ensure};
6use async_trait::async_trait;
7use fedimint_core::PeerId;
8use fedimint_core::config::PeerUrl;
9use fedimint_core::encoding::{Decodable, Encodable};
10use fedimint_core::util::SafeUrl;
11use fedimint_server_core::dashboard_ui::ConnectionType;
12use rustls::pki_types::ServerName;
13use serde::Serialize;
14use serde::de::DeserializeOwned;
15use tokio::net::{TcpListener, TcpStream};
16use tokio_rustls::rustls::RootCertStore;
17use tokio_rustls::rustls::server::WebPkiClientVerifier;
18use tokio_rustls::{TlsAcceptor, TlsConnector, TlsStream, rustls};
19use tokio_util::codec::LengthDelimitedCodec;
20
21use super::IP2PConnector;
22use super::iroh::parse_p2p;
23use crate::net::p2p_connection::{DynP2PConnection, IP2PConnection as _};
24
25#[derive(Debug, Clone)]
26pub struct TlsConfig {
27    pub private_key: Arc<rustls::pki_types::PrivateKeyDer<'static>>,
28    pub certificates: BTreeMap<PeerId, rustls::pki_types::CertificateDer<'static>>,
29    pub peer_names: BTreeMap<PeerId, String>,
30}
31
32/// TCP connector with encryption and authentication
33pub struct TlsTcpConnector {
34    pub(crate) cfg: TlsConfig,
35    pub(crate) peers: BTreeMap<PeerId, SafeUrl>,
36    pub(crate) identity: PeerId,
37    pub(crate) listener: TcpListener,
38    pub(crate) acceptor: TlsAcceptor,
39}
40
41impl TlsTcpConnector {
42    pub async fn new(
43        cfg: TlsConfig,
44        p2p_bind_addr: SocketAddr,
45        peers: BTreeMap<PeerId, PeerUrl>,
46        identity: PeerId,
47    ) -> TlsTcpConnector {
48        let mut root_cert_store = RootCertStore::empty();
49
50        for cert in cfg.certificates.values() {
51            root_cert_store
52                .add(cert.clone())
53                .expect("Could not add peer certificate");
54        }
55
56        let verifier = WebPkiClientVerifier::builder(root_cert_store.into())
57            .build()
58            .expect("Failed to create client verifier");
59
60        let certificate = cfg
61            .certificates
62            .get(&identity)
63            .expect("No certificate for ourself found")
64            .clone();
65
66        let config = rustls::ServerConfig::builder()
67            .with_client_cert_verifier(verifier)
68            .with_single_cert(vec![certificate], cfg.private_key.clone_key())
69            .expect("Failed to create TLS config");
70
71        let listener = TcpListener::bind(p2p_bind_addr)
72            .await
73            .expect("Could not bind to port");
74
75        let acceptor = TlsAcceptor::from(Arc::new(config.clone()));
76
77        TlsTcpConnector {
78            cfg,
79            peers: peers.into_iter().map(|(id, peer)| (id, peer.url)).collect(),
80            identity,
81            listener,
82            acceptor,
83        }
84    }
85}
86
87#[async_trait]
88impl<M> IP2PConnector<M> for TlsTcpConnector
89where
90    M: Encodable + Decodable + Serialize + DeserializeOwned + Send + 'static,
91{
92    fn peers(&self) -> Vec<PeerId> {
93        self.peers
94            .keys()
95            .filter(|peer| **peer != self.identity)
96            .copied()
97            .collect()
98    }
99
100    async fn connect(&self, peer: PeerId) -> anyhow::Result<DynP2PConnection<M>> {
101        let mut root_cert_store = RootCertStore::empty();
102
103        for cert in self.cfg.certificates.values() {
104            root_cert_store
105                .add(cert.clone())
106                .expect("Could not add peer certificate");
107        }
108
109        let certificate = self
110            .cfg
111            .certificates
112            .get(&self.identity)
113            .expect("No certificate for ourself found")
114            .clone();
115
116        let cfg = rustls::ClientConfig::builder()
117            .with_root_certificates(root_cert_store)
118            .with_client_auth_cert(vec![certificate], self.cfg.private_key.clone_key())
119            .expect("Failed to create TLS config");
120
121        let domain = ServerName::try_from(dns_sanitize(&self.cfg.peer_names[&peer]))
122            .expect("Always a valid DNS name");
123
124        let destination = self.peers.get(&peer).expect("No url for peer");
125
126        let tls = TlsConnector::from(Arc::new(cfg))
127            .connect(domain, TcpStream::connect(parse_p2p(destination)?).await?)
128            .await?;
129
130        let certificate = tls
131            .get_ref()
132            .1
133            .peer_certificates()
134            .context("Peer did not authenticate itself")?
135            .first()
136            .context("Received certificate chain of length zero")?;
137
138        let auth_peer = self
139            .cfg
140            .certificates
141            .iter()
142            .find_map(|(peer, c)| if c == certificate { Some(*peer) } else { None })
143            .context("Unknown certificate")?;
144
145        ensure!(auth_peer == peer, "Connected to unexpected peer");
146
147        let framed = LengthDelimitedCodec::builder()
148            .length_field_type::<u64>()
149            .new_framed(TlsStream::Client(tls));
150
151        Ok(framed.into_dyn())
152    }
153
154    async fn accept(&self) -> anyhow::Result<(PeerId, DynP2PConnection<M>)> {
155        let tls = self
156            .acceptor
157            .accept(self.listener.accept().await?.0)
158            .await?;
159
160        let certificate = tls
161            .get_ref()
162            .1
163            .peer_certificates()
164            .context("Peer did not authenticate itself")?
165            .first()
166            .context("Received certificate chain of length zero")?;
167
168        let auth_peer = self
169            .cfg
170            .certificates
171            .iter()
172            .find_map(|(peer, c)| if c == certificate { Some(*peer) } else { None })
173            .context("Unknown certificate")?;
174
175        let framed = LengthDelimitedCodec::builder()
176            .length_field_type::<u64>()
177            .new_framed(TlsStream::Server(tls));
178
179        Ok((auth_peer, framed.into_dyn()))
180    }
181
182    async fn connection_type(&self, _peer: PeerId) -> ConnectionType {
183        // TLS connections are always direct
184        ConnectionType::Direct
185    }
186}
187
188pub fn gen_cert_and_key(
189    name: &str,
190) -> Result<
191    (
192        rustls::pki_types::CertificateDer<'static>,
193        Arc<rustls::pki_types::PrivateKeyDer<'static>>,
194    ),
195    anyhow::Error,
196> {
197    let cert_key = rcgen::generate_simple_self_signed(vec![dns_sanitize(name)])?;
198
199    Ok((
200        rustls::pki_types::CertificateDer::from(cert_key.cert.der().to_vec()),
201        Arc::new(
202            rustls::pki_types::PrivateKeyDer::try_from(cert_key.key_pair.serialize_der())
203                .expect("Failed to create private key"),
204        ),
205    ))
206}
207
208/// Sanitizes name as valid domain name
209pub fn dns_sanitize(name: &str) -> String {
210    format!(
211        "peer{}",
212        name.replace(|c: char| !c.is_ascii_alphanumeric(), "_")
213    )
214}