Skip to main content

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